18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2013-2017 ARM Limited, All Rights Reserved.
48c2ecf20Sopenharmony_ci * Author: Marc Zyngier <marc.zyngier@arm.com>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/acpi.h>
88c2ecf20Sopenharmony_ci#include <linux/acpi_iort.h>
98c2ecf20Sopenharmony_ci#include <linux/bitfield.h>
108c2ecf20Sopenharmony_ci#include <linux/bitmap.h>
118c2ecf20Sopenharmony_ci#include <linux/cpu.h>
128c2ecf20Sopenharmony_ci#include <linux/crash_dump.h>
138c2ecf20Sopenharmony_ci#include <linux/delay.h>
148c2ecf20Sopenharmony_ci#include <linux/dma-iommu.h>
158c2ecf20Sopenharmony_ci#include <linux/efi.h>
168c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
178c2ecf20Sopenharmony_ci#include <linux/iopoll.h>
188c2ecf20Sopenharmony_ci#include <linux/irqdomain.h>
198c2ecf20Sopenharmony_ci#include <linux/list.h>
208c2ecf20Sopenharmony_ci#include <linux/log2.h>
218c2ecf20Sopenharmony_ci#include <linux/memblock.h>
228c2ecf20Sopenharmony_ci#include <linux/mm.h>
238c2ecf20Sopenharmony_ci#include <linux/msi.h>
248c2ecf20Sopenharmony_ci#include <linux/of.h>
258c2ecf20Sopenharmony_ci#include <linux/of_address.h>
268c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
278c2ecf20Sopenharmony_ci#include <linux/of_pci.h>
288c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
298c2ecf20Sopenharmony_ci#include <linux/percpu.h>
308c2ecf20Sopenharmony_ci#include <linux/slab.h>
318c2ecf20Sopenharmony_ci#include <linux/syscore_ops.h>
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#include <linux/irqchip.h>
348c2ecf20Sopenharmony_ci#include <linux/irqchip/arm-gic-v3.h>
358c2ecf20Sopenharmony_ci#include <linux/irqchip/arm-gic-v4.h>
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#include <asm/cputype.h>
388c2ecf20Sopenharmony_ci#include <asm/exception.h>
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#include "irq-gic-common.h"
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#define ITS_FLAGS_CMDQ_NEEDS_FLUSHING		(1ULL << 0)
438c2ecf20Sopenharmony_ci#define ITS_FLAGS_WORKAROUND_CAVIUM_22375	(1ULL << 1)
448c2ecf20Sopenharmony_ci#define ITS_FLAGS_WORKAROUND_CAVIUM_23144	(1ULL << 2)
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#define RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING	(1 << 0)
478c2ecf20Sopenharmony_ci#define RDIST_FLAGS_RD_TABLES_PREALLOCATED	(1 << 1)
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic u32 lpi_id_bits;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci/*
528c2ecf20Sopenharmony_ci * We allocate memory for PROPBASE to cover 2 ^ lpi_id_bits LPIs to
538c2ecf20Sopenharmony_ci * deal with (one configuration byte per interrupt). PENDBASE has to
548c2ecf20Sopenharmony_ci * be 64kB aligned (one bit per LPI, plus 8192 bits for SPI/PPI/SGI).
558c2ecf20Sopenharmony_ci */
568c2ecf20Sopenharmony_ci#define LPI_NRBITS		lpi_id_bits
578c2ecf20Sopenharmony_ci#define LPI_PROPBASE_SZ		ALIGN(BIT(LPI_NRBITS), SZ_64K)
588c2ecf20Sopenharmony_ci#define LPI_PENDBASE_SZ		ALIGN(BIT(LPI_NRBITS) / 8, SZ_64K)
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci#define LPI_PROP_DEFAULT_PRIO	GICD_INT_DEF_PRI
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci/*
638c2ecf20Sopenharmony_ci * Collection structure - just an ID, and a redistributor address to
648c2ecf20Sopenharmony_ci * ping. We use one per CPU as a bag of interrupts assigned to this
658c2ecf20Sopenharmony_ci * CPU.
668c2ecf20Sopenharmony_ci */
678c2ecf20Sopenharmony_cistruct its_collection {
688c2ecf20Sopenharmony_ci	u64			target_address;
698c2ecf20Sopenharmony_ci	u16			col_id;
708c2ecf20Sopenharmony_ci};
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci/*
738c2ecf20Sopenharmony_ci * The ITS_BASER structure - contains memory information, cached
748c2ecf20Sopenharmony_ci * value of BASER register configuration and ITS page size.
758c2ecf20Sopenharmony_ci */
768c2ecf20Sopenharmony_cistruct its_baser {
778c2ecf20Sopenharmony_ci	void		*base;
788c2ecf20Sopenharmony_ci	u64		val;
798c2ecf20Sopenharmony_ci	u32		order;
808c2ecf20Sopenharmony_ci	u32		psz;
818c2ecf20Sopenharmony_ci};
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistruct its_device;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci/*
868c2ecf20Sopenharmony_ci * The ITS structure - contains most of the infrastructure, with the
878c2ecf20Sopenharmony_ci * top-level MSI domain, the command queue, the collections, and the
888c2ecf20Sopenharmony_ci * list of devices writing to it.
898c2ecf20Sopenharmony_ci *
908c2ecf20Sopenharmony_ci * dev_alloc_lock has to be taken for device allocations, while the
918c2ecf20Sopenharmony_ci * spinlock must be taken to parse data structures such as the device
928c2ecf20Sopenharmony_ci * list.
938c2ecf20Sopenharmony_ci */
948c2ecf20Sopenharmony_cistruct its_node {
958c2ecf20Sopenharmony_ci	raw_spinlock_t		lock;
968c2ecf20Sopenharmony_ci	struct mutex		dev_alloc_lock;
978c2ecf20Sopenharmony_ci	struct list_head	entry;
988c2ecf20Sopenharmony_ci	void __iomem		*base;
998c2ecf20Sopenharmony_ci	void __iomem		*sgir_base;
1008c2ecf20Sopenharmony_ci	phys_addr_t		phys_base;
1018c2ecf20Sopenharmony_ci	struct its_cmd_block	*cmd_base;
1028c2ecf20Sopenharmony_ci	struct its_cmd_block	*cmd_write;
1038c2ecf20Sopenharmony_ci	struct its_baser	tables[GITS_BASER_NR_REGS];
1048c2ecf20Sopenharmony_ci	struct its_collection	*collections;
1058c2ecf20Sopenharmony_ci	struct fwnode_handle	*fwnode_handle;
1068c2ecf20Sopenharmony_ci	u64			(*get_msi_base)(struct its_device *its_dev);
1078c2ecf20Sopenharmony_ci	u64			typer;
1088c2ecf20Sopenharmony_ci	u64			cbaser_save;
1098c2ecf20Sopenharmony_ci	u32			ctlr_save;
1108c2ecf20Sopenharmony_ci	u32			mpidr;
1118c2ecf20Sopenharmony_ci	struct list_head	its_device_list;
1128c2ecf20Sopenharmony_ci	u64			flags;
1138c2ecf20Sopenharmony_ci	unsigned long		list_nr;
1148c2ecf20Sopenharmony_ci	int			numa_node;
1158c2ecf20Sopenharmony_ci	unsigned int		msi_domain_flags;
1168c2ecf20Sopenharmony_ci	u32			pre_its_base; /* for Socionext Synquacer */
1178c2ecf20Sopenharmony_ci	int			vlpi_redist_offset;
1188c2ecf20Sopenharmony_ci};
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci#define is_v4(its)		(!!((its)->typer & GITS_TYPER_VLPIS))
1218c2ecf20Sopenharmony_ci#define is_v4_1(its)		(!!((its)->typer & GITS_TYPER_VMAPP))
1228c2ecf20Sopenharmony_ci#define device_ids(its)		(FIELD_GET(GITS_TYPER_DEVBITS, (its)->typer) + 1)
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci#define ITS_ITT_ALIGN		SZ_256
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci/* The maximum number of VPEID bits supported by VLPI commands */
1278c2ecf20Sopenharmony_ci#define ITS_MAX_VPEID_BITS						\
1288c2ecf20Sopenharmony_ci	({								\
1298c2ecf20Sopenharmony_ci		int nvpeid = 16;					\
1308c2ecf20Sopenharmony_ci		if (gic_rdists->has_rvpeid &&				\
1318c2ecf20Sopenharmony_ci		    gic_rdists->gicd_typer2 & GICD_TYPER2_VIL)		\
1328c2ecf20Sopenharmony_ci			nvpeid = 1 + (gic_rdists->gicd_typer2 &		\
1338c2ecf20Sopenharmony_ci				      GICD_TYPER2_VID);			\
1348c2ecf20Sopenharmony_ci									\
1358c2ecf20Sopenharmony_ci		nvpeid;							\
1368c2ecf20Sopenharmony_ci	})
1378c2ecf20Sopenharmony_ci#define ITS_MAX_VPEID		(1 << (ITS_MAX_VPEID_BITS))
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci/* Convert page order to size in bytes */
1408c2ecf20Sopenharmony_ci#define PAGE_ORDER_TO_SIZE(o)	(PAGE_SIZE << (o))
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistruct event_lpi_map {
1438c2ecf20Sopenharmony_ci	unsigned long		*lpi_map;
1448c2ecf20Sopenharmony_ci	u16			*col_map;
1458c2ecf20Sopenharmony_ci	irq_hw_number_t		lpi_base;
1468c2ecf20Sopenharmony_ci	int			nr_lpis;
1478c2ecf20Sopenharmony_ci	raw_spinlock_t		vlpi_lock;
1488c2ecf20Sopenharmony_ci	struct its_vm		*vm;
1498c2ecf20Sopenharmony_ci	struct its_vlpi_map	*vlpi_maps;
1508c2ecf20Sopenharmony_ci	int			nr_vlpis;
1518c2ecf20Sopenharmony_ci};
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci/*
1548c2ecf20Sopenharmony_ci * The ITS view of a device - belongs to an ITS, owns an interrupt
1558c2ecf20Sopenharmony_ci * translation table, and a list of interrupts.  If it some of its
1568c2ecf20Sopenharmony_ci * LPIs are injected into a guest (GICv4), the event_map.vm field
1578c2ecf20Sopenharmony_ci * indicates which one.
1588c2ecf20Sopenharmony_ci */
1598c2ecf20Sopenharmony_cistruct its_device {
1608c2ecf20Sopenharmony_ci	struct list_head	entry;
1618c2ecf20Sopenharmony_ci	struct its_node		*its;
1628c2ecf20Sopenharmony_ci	struct event_lpi_map	event_map;
1638c2ecf20Sopenharmony_ci	void			*itt;
1648c2ecf20Sopenharmony_ci	u32			nr_ites;
1658c2ecf20Sopenharmony_ci	u32			device_id;
1668c2ecf20Sopenharmony_ci	bool			shared;
1678c2ecf20Sopenharmony_ci};
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic struct {
1708c2ecf20Sopenharmony_ci	raw_spinlock_t		lock;
1718c2ecf20Sopenharmony_ci	struct its_device	*dev;
1728c2ecf20Sopenharmony_ci	struct its_vpe		**vpes;
1738c2ecf20Sopenharmony_ci	int			next_victim;
1748c2ecf20Sopenharmony_ci} vpe_proxy;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistruct cpu_lpi_count {
1778c2ecf20Sopenharmony_ci	atomic_t	managed;
1788c2ecf20Sopenharmony_ci	atomic_t	unmanaged;
1798c2ecf20Sopenharmony_ci};
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU(struct cpu_lpi_count, cpu_lpi_count);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic LIST_HEAD(its_nodes);
1848c2ecf20Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(its_lock);
1858c2ecf20Sopenharmony_cistatic struct rdists *gic_rdists;
1868c2ecf20Sopenharmony_cistatic struct irq_domain *its_parent;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic unsigned long its_list_map;
1898c2ecf20Sopenharmony_cistatic u16 vmovp_seq_num;
1908c2ecf20Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(vmovp_lock);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic DEFINE_IDA(its_vpeid_ida);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci#define gic_data_rdist()		(raw_cpu_ptr(gic_rdists->rdist))
1958c2ecf20Sopenharmony_ci#define gic_data_rdist_cpu(cpu)		(per_cpu_ptr(gic_rdists->rdist, cpu))
1968c2ecf20Sopenharmony_ci#define gic_data_rdist_rd_base()	(gic_data_rdist()->rd_base)
1978c2ecf20Sopenharmony_ci#define gic_data_rdist_vlpi_base()	(gic_data_rdist_rd_base() + SZ_128K)
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci/*
2008c2ecf20Sopenharmony_ci * Skip ITSs that have no vLPIs mapped, unless we're on GICv4.1, as we
2018c2ecf20Sopenharmony_ci * always have vSGIs mapped.
2028c2ecf20Sopenharmony_ci */
2038c2ecf20Sopenharmony_cistatic bool require_its_list_vmovp(struct its_vm *vm, struct its_node *its)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	return (gic_rdists->has_rvpeid || vm->vlpi_count[its->list_nr]);
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistatic u16 get_its_list(struct its_vm *vm)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	struct its_node *its;
2118c2ecf20Sopenharmony_ci	unsigned long its_list = 0;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	list_for_each_entry(its, &its_nodes, entry) {
2148c2ecf20Sopenharmony_ci		if (!is_v4(its))
2158c2ecf20Sopenharmony_ci			continue;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci		if (require_its_list_vmovp(vm, its))
2188c2ecf20Sopenharmony_ci			__set_bit(its->list_nr, &its_list);
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	return (u16)its_list;
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cistatic inline u32 its_get_event_id(struct irq_data *d)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
2278c2ecf20Sopenharmony_ci	return d->hwirq - its_dev->event_map.lpi_base;
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistatic struct its_collection *dev_event_to_col(struct its_device *its_dev,
2318c2ecf20Sopenharmony_ci					       u32 event)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	struct its_node *its = its_dev->its;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	return its->collections + its_dev->event_map.col_map[event];
2368c2ecf20Sopenharmony_ci}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_cistatic struct its_vlpi_map *dev_event_to_vlpi_map(struct its_device *its_dev,
2398c2ecf20Sopenharmony_ci					       u32 event)
2408c2ecf20Sopenharmony_ci{
2418c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(event >= its_dev->event_map.nr_lpis))
2428c2ecf20Sopenharmony_ci		return NULL;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	return &its_dev->event_map.vlpi_maps[event];
2458c2ecf20Sopenharmony_ci}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_cistatic struct its_vlpi_map *get_vlpi_map(struct irq_data *d)
2488c2ecf20Sopenharmony_ci{
2498c2ecf20Sopenharmony_ci	if (irqd_is_forwarded_to_vcpu(d)) {
2508c2ecf20Sopenharmony_ci		struct its_device *its_dev = irq_data_get_irq_chip_data(d);
2518c2ecf20Sopenharmony_ci		u32 event = its_get_event_id(d);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci		return dev_event_to_vlpi_map(its_dev, event);
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	return NULL;
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_cistatic int vpe_to_cpuid_lock(struct its_vpe *vpe, unsigned long *flags)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&vpe->vpe_lock, *flags);
2628c2ecf20Sopenharmony_ci	return vpe->col_idx;
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistatic void vpe_to_cpuid_unlock(struct its_vpe *vpe, unsigned long flags)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&vpe->vpe_lock, flags);
2688c2ecf20Sopenharmony_ci}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_cistatic struct irq_chip its_vpe_irq_chip;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_cistatic int irq_to_cpuid_lock(struct irq_data *d, unsigned long *flags)
2738c2ecf20Sopenharmony_ci{
2748c2ecf20Sopenharmony_ci	struct its_vpe *vpe = NULL;
2758c2ecf20Sopenharmony_ci	int cpu;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	if (d->chip == &its_vpe_irq_chip) {
2788c2ecf20Sopenharmony_ci		vpe = irq_data_get_irq_chip_data(d);
2798c2ecf20Sopenharmony_ci	} else {
2808c2ecf20Sopenharmony_ci		struct its_vlpi_map *map = get_vlpi_map(d);
2818c2ecf20Sopenharmony_ci		if (map)
2828c2ecf20Sopenharmony_ci			vpe = map->vpe;
2838c2ecf20Sopenharmony_ci	}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	if (vpe) {
2868c2ecf20Sopenharmony_ci		cpu = vpe_to_cpuid_lock(vpe, flags);
2878c2ecf20Sopenharmony_ci	} else {
2888c2ecf20Sopenharmony_ci		/* Physical LPIs are already locked via the irq_desc lock */
2898c2ecf20Sopenharmony_ci		struct its_device *its_dev = irq_data_get_irq_chip_data(d);
2908c2ecf20Sopenharmony_ci		cpu = its_dev->event_map.col_map[its_get_event_id(d)];
2918c2ecf20Sopenharmony_ci		/* Keep GCC quiet... */
2928c2ecf20Sopenharmony_ci		*flags = 0;
2938c2ecf20Sopenharmony_ci	}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	return cpu;
2968c2ecf20Sopenharmony_ci}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_cistatic void irq_to_cpuid_unlock(struct irq_data *d, unsigned long flags)
2998c2ecf20Sopenharmony_ci{
3008c2ecf20Sopenharmony_ci	struct its_vpe *vpe = NULL;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	if (d->chip == &its_vpe_irq_chip) {
3038c2ecf20Sopenharmony_ci		vpe = irq_data_get_irq_chip_data(d);
3048c2ecf20Sopenharmony_ci	} else {
3058c2ecf20Sopenharmony_ci		struct its_vlpi_map *map = get_vlpi_map(d);
3068c2ecf20Sopenharmony_ci		if (map)
3078c2ecf20Sopenharmony_ci			vpe = map->vpe;
3088c2ecf20Sopenharmony_ci	}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	if (vpe)
3118c2ecf20Sopenharmony_ci		vpe_to_cpuid_unlock(vpe, flags);
3128c2ecf20Sopenharmony_ci}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_cistatic struct its_collection *valid_col(struct its_collection *col)
3158c2ecf20Sopenharmony_ci{
3168c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(col->target_address & GENMASK_ULL(15, 0)))
3178c2ecf20Sopenharmony_ci		return NULL;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	return col;
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_cistatic struct its_vpe *valid_vpe(struct its_node *its, struct its_vpe *vpe)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	if (valid_col(its->collections + vpe->col_idx))
3258c2ecf20Sopenharmony_ci		return vpe;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	return NULL;
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci/*
3318c2ecf20Sopenharmony_ci * ITS command descriptors - parameters to be encoded in a command
3328c2ecf20Sopenharmony_ci * block.
3338c2ecf20Sopenharmony_ci */
3348c2ecf20Sopenharmony_cistruct its_cmd_desc {
3358c2ecf20Sopenharmony_ci	union {
3368c2ecf20Sopenharmony_ci		struct {
3378c2ecf20Sopenharmony_ci			struct its_device *dev;
3388c2ecf20Sopenharmony_ci			u32 event_id;
3398c2ecf20Sopenharmony_ci		} its_inv_cmd;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci		struct {
3428c2ecf20Sopenharmony_ci			struct its_device *dev;
3438c2ecf20Sopenharmony_ci			u32 event_id;
3448c2ecf20Sopenharmony_ci		} its_clear_cmd;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci		struct {
3478c2ecf20Sopenharmony_ci			struct its_device *dev;
3488c2ecf20Sopenharmony_ci			u32 event_id;
3498c2ecf20Sopenharmony_ci		} its_int_cmd;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci		struct {
3528c2ecf20Sopenharmony_ci			struct its_device *dev;
3538c2ecf20Sopenharmony_ci			int valid;
3548c2ecf20Sopenharmony_ci		} its_mapd_cmd;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci		struct {
3578c2ecf20Sopenharmony_ci			struct its_collection *col;
3588c2ecf20Sopenharmony_ci			int valid;
3598c2ecf20Sopenharmony_ci		} its_mapc_cmd;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci		struct {
3628c2ecf20Sopenharmony_ci			struct its_device *dev;
3638c2ecf20Sopenharmony_ci			u32 phys_id;
3648c2ecf20Sopenharmony_ci			u32 event_id;
3658c2ecf20Sopenharmony_ci		} its_mapti_cmd;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci		struct {
3688c2ecf20Sopenharmony_ci			struct its_device *dev;
3698c2ecf20Sopenharmony_ci			struct its_collection *col;
3708c2ecf20Sopenharmony_ci			u32 event_id;
3718c2ecf20Sopenharmony_ci		} its_movi_cmd;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci		struct {
3748c2ecf20Sopenharmony_ci			struct its_device *dev;
3758c2ecf20Sopenharmony_ci			u32 event_id;
3768c2ecf20Sopenharmony_ci		} its_discard_cmd;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci		struct {
3798c2ecf20Sopenharmony_ci			struct its_collection *col;
3808c2ecf20Sopenharmony_ci		} its_invall_cmd;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci		struct {
3838c2ecf20Sopenharmony_ci			struct its_vpe *vpe;
3848c2ecf20Sopenharmony_ci		} its_vinvall_cmd;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci		struct {
3878c2ecf20Sopenharmony_ci			struct its_vpe *vpe;
3888c2ecf20Sopenharmony_ci			struct its_collection *col;
3898c2ecf20Sopenharmony_ci			bool valid;
3908c2ecf20Sopenharmony_ci		} its_vmapp_cmd;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci		struct {
3938c2ecf20Sopenharmony_ci			struct its_vpe *vpe;
3948c2ecf20Sopenharmony_ci			struct its_device *dev;
3958c2ecf20Sopenharmony_ci			u32 virt_id;
3968c2ecf20Sopenharmony_ci			u32 event_id;
3978c2ecf20Sopenharmony_ci			bool db_enabled;
3988c2ecf20Sopenharmony_ci		} its_vmapti_cmd;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci		struct {
4018c2ecf20Sopenharmony_ci			struct its_vpe *vpe;
4028c2ecf20Sopenharmony_ci			struct its_device *dev;
4038c2ecf20Sopenharmony_ci			u32 event_id;
4048c2ecf20Sopenharmony_ci			bool db_enabled;
4058c2ecf20Sopenharmony_ci		} its_vmovi_cmd;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci		struct {
4088c2ecf20Sopenharmony_ci			struct its_vpe *vpe;
4098c2ecf20Sopenharmony_ci			struct its_collection *col;
4108c2ecf20Sopenharmony_ci			u16 seq_num;
4118c2ecf20Sopenharmony_ci			u16 its_list;
4128c2ecf20Sopenharmony_ci		} its_vmovp_cmd;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci		struct {
4158c2ecf20Sopenharmony_ci			struct its_vpe *vpe;
4168c2ecf20Sopenharmony_ci		} its_invdb_cmd;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci		struct {
4198c2ecf20Sopenharmony_ci			struct its_vpe *vpe;
4208c2ecf20Sopenharmony_ci			u8 sgi;
4218c2ecf20Sopenharmony_ci			u8 priority;
4228c2ecf20Sopenharmony_ci			bool enable;
4238c2ecf20Sopenharmony_ci			bool group;
4248c2ecf20Sopenharmony_ci			bool clear;
4258c2ecf20Sopenharmony_ci		} its_vsgi_cmd;
4268c2ecf20Sopenharmony_ci	};
4278c2ecf20Sopenharmony_ci};
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci/*
4308c2ecf20Sopenharmony_ci * The ITS command block, which is what the ITS actually parses.
4318c2ecf20Sopenharmony_ci */
4328c2ecf20Sopenharmony_cistruct its_cmd_block {
4338c2ecf20Sopenharmony_ci	union {
4348c2ecf20Sopenharmony_ci		u64	raw_cmd[4];
4358c2ecf20Sopenharmony_ci		__le64	raw_cmd_le[4];
4368c2ecf20Sopenharmony_ci	};
4378c2ecf20Sopenharmony_ci};
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci#define ITS_CMD_QUEUE_SZ		SZ_64K
4408c2ecf20Sopenharmony_ci#define ITS_CMD_QUEUE_NR_ENTRIES	(ITS_CMD_QUEUE_SZ / sizeof(struct its_cmd_block))
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_citypedef struct its_collection *(*its_cmd_builder_t)(struct its_node *,
4438c2ecf20Sopenharmony_ci						    struct its_cmd_block *,
4448c2ecf20Sopenharmony_ci						    struct its_cmd_desc *);
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_citypedef struct its_vpe *(*its_cmd_vbuilder_t)(struct its_node *,
4478c2ecf20Sopenharmony_ci					      struct its_cmd_block *,
4488c2ecf20Sopenharmony_ci					      struct its_cmd_desc *);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_cistatic void its_mask_encode(u64 *raw_cmd, u64 val, int h, int l)
4518c2ecf20Sopenharmony_ci{
4528c2ecf20Sopenharmony_ci	u64 mask = GENMASK_ULL(h, l);
4538c2ecf20Sopenharmony_ci	*raw_cmd &= ~mask;
4548c2ecf20Sopenharmony_ci	*raw_cmd |= (val << l) & mask;
4558c2ecf20Sopenharmony_ci}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_cistatic void its_encode_cmd(struct its_cmd_block *cmd, u8 cmd_nr)
4588c2ecf20Sopenharmony_ci{
4598c2ecf20Sopenharmony_ci	its_mask_encode(&cmd->raw_cmd[0], cmd_nr, 7, 0);
4608c2ecf20Sopenharmony_ci}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_cistatic void its_encode_devid(struct its_cmd_block *cmd, u32 devid)
4638c2ecf20Sopenharmony_ci{
4648c2ecf20Sopenharmony_ci	its_mask_encode(&cmd->raw_cmd[0], devid, 63, 32);
4658c2ecf20Sopenharmony_ci}
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_cistatic void its_encode_event_id(struct its_cmd_block *cmd, u32 id)
4688c2ecf20Sopenharmony_ci{
4698c2ecf20Sopenharmony_ci	its_mask_encode(&cmd->raw_cmd[1], id, 31, 0);
4708c2ecf20Sopenharmony_ci}
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_cistatic void its_encode_phys_id(struct its_cmd_block *cmd, u32 phys_id)
4738c2ecf20Sopenharmony_ci{
4748c2ecf20Sopenharmony_ci	its_mask_encode(&cmd->raw_cmd[1], phys_id, 63, 32);
4758c2ecf20Sopenharmony_ci}
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_cistatic void its_encode_size(struct its_cmd_block *cmd, u8 size)
4788c2ecf20Sopenharmony_ci{
4798c2ecf20Sopenharmony_ci	its_mask_encode(&cmd->raw_cmd[1], size, 4, 0);
4808c2ecf20Sopenharmony_ci}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_cistatic void its_encode_itt(struct its_cmd_block *cmd, u64 itt_addr)
4838c2ecf20Sopenharmony_ci{
4848c2ecf20Sopenharmony_ci	its_mask_encode(&cmd->raw_cmd[2], itt_addr >> 8, 51, 8);
4858c2ecf20Sopenharmony_ci}
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_cistatic void its_encode_valid(struct its_cmd_block *cmd, int valid)
4888c2ecf20Sopenharmony_ci{
4898c2ecf20Sopenharmony_ci	its_mask_encode(&cmd->raw_cmd[2], !!valid, 63, 63);
4908c2ecf20Sopenharmony_ci}
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_cistatic void its_encode_target(struct its_cmd_block *cmd, u64 target_addr)
4938c2ecf20Sopenharmony_ci{
4948c2ecf20Sopenharmony_ci	its_mask_encode(&cmd->raw_cmd[2], target_addr >> 16, 51, 16);
4958c2ecf20Sopenharmony_ci}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_cistatic void its_encode_collection(struct its_cmd_block *cmd, u16 col)
4988c2ecf20Sopenharmony_ci{
4998c2ecf20Sopenharmony_ci	its_mask_encode(&cmd->raw_cmd[2], col, 15, 0);
5008c2ecf20Sopenharmony_ci}
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_cistatic void its_encode_vpeid(struct its_cmd_block *cmd, u16 vpeid)
5038c2ecf20Sopenharmony_ci{
5048c2ecf20Sopenharmony_ci	its_mask_encode(&cmd->raw_cmd[1], vpeid, 47, 32);
5058c2ecf20Sopenharmony_ci}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_cistatic void its_encode_virt_id(struct its_cmd_block *cmd, u32 virt_id)
5088c2ecf20Sopenharmony_ci{
5098c2ecf20Sopenharmony_ci	its_mask_encode(&cmd->raw_cmd[2], virt_id, 31, 0);
5108c2ecf20Sopenharmony_ci}
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_cistatic void its_encode_db_phys_id(struct its_cmd_block *cmd, u32 db_phys_id)
5138c2ecf20Sopenharmony_ci{
5148c2ecf20Sopenharmony_ci	its_mask_encode(&cmd->raw_cmd[2], db_phys_id, 63, 32);
5158c2ecf20Sopenharmony_ci}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_cistatic void its_encode_db_valid(struct its_cmd_block *cmd, bool db_valid)
5188c2ecf20Sopenharmony_ci{
5198c2ecf20Sopenharmony_ci	its_mask_encode(&cmd->raw_cmd[2], db_valid, 0, 0);
5208c2ecf20Sopenharmony_ci}
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_cistatic void its_encode_seq_num(struct its_cmd_block *cmd, u16 seq_num)
5238c2ecf20Sopenharmony_ci{
5248c2ecf20Sopenharmony_ci	its_mask_encode(&cmd->raw_cmd[0], seq_num, 47, 32);
5258c2ecf20Sopenharmony_ci}
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_cistatic void its_encode_its_list(struct its_cmd_block *cmd, u16 its_list)
5288c2ecf20Sopenharmony_ci{
5298c2ecf20Sopenharmony_ci	its_mask_encode(&cmd->raw_cmd[1], its_list, 15, 0);
5308c2ecf20Sopenharmony_ci}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_cistatic void its_encode_vpt_addr(struct its_cmd_block *cmd, u64 vpt_pa)
5338c2ecf20Sopenharmony_ci{
5348c2ecf20Sopenharmony_ci	its_mask_encode(&cmd->raw_cmd[3], vpt_pa >> 16, 51, 16);
5358c2ecf20Sopenharmony_ci}
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_cistatic void its_encode_vpt_size(struct its_cmd_block *cmd, u8 vpt_size)
5388c2ecf20Sopenharmony_ci{
5398c2ecf20Sopenharmony_ci	its_mask_encode(&cmd->raw_cmd[3], vpt_size, 4, 0);
5408c2ecf20Sopenharmony_ci}
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_cistatic void its_encode_vconf_addr(struct its_cmd_block *cmd, u64 vconf_pa)
5438c2ecf20Sopenharmony_ci{
5448c2ecf20Sopenharmony_ci	its_mask_encode(&cmd->raw_cmd[0], vconf_pa >> 16, 51, 16);
5458c2ecf20Sopenharmony_ci}
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_cistatic void its_encode_alloc(struct its_cmd_block *cmd, bool alloc)
5488c2ecf20Sopenharmony_ci{
5498c2ecf20Sopenharmony_ci	its_mask_encode(&cmd->raw_cmd[0], alloc, 8, 8);
5508c2ecf20Sopenharmony_ci}
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_cistatic void its_encode_ptz(struct its_cmd_block *cmd, bool ptz)
5538c2ecf20Sopenharmony_ci{
5548c2ecf20Sopenharmony_ci	its_mask_encode(&cmd->raw_cmd[0], ptz, 9, 9);
5558c2ecf20Sopenharmony_ci}
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_cistatic void its_encode_vmapp_default_db(struct its_cmd_block *cmd,
5588c2ecf20Sopenharmony_ci					u32 vpe_db_lpi)
5598c2ecf20Sopenharmony_ci{
5608c2ecf20Sopenharmony_ci	its_mask_encode(&cmd->raw_cmd[1], vpe_db_lpi, 31, 0);
5618c2ecf20Sopenharmony_ci}
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_cistatic void its_encode_vmovp_default_db(struct its_cmd_block *cmd,
5648c2ecf20Sopenharmony_ci					u32 vpe_db_lpi)
5658c2ecf20Sopenharmony_ci{
5668c2ecf20Sopenharmony_ci	its_mask_encode(&cmd->raw_cmd[3], vpe_db_lpi, 31, 0);
5678c2ecf20Sopenharmony_ci}
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_cistatic void its_encode_db(struct its_cmd_block *cmd, bool db)
5708c2ecf20Sopenharmony_ci{
5718c2ecf20Sopenharmony_ci	its_mask_encode(&cmd->raw_cmd[2], db, 63, 63);
5728c2ecf20Sopenharmony_ci}
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_cistatic void its_encode_sgi_intid(struct its_cmd_block *cmd, u8 sgi)
5758c2ecf20Sopenharmony_ci{
5768c2ecf20Sopenharmony_ci	its_mask_encode(&cmd->raw_cmd[0], sgi, 35, 32);
5778c2ecf20Sopenharmony_ci}
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_cistatic void its_encode_sgi_priority(struct its_cmd_block *cmd, u8 prio)
5808c2ecf20Sopenharmony_ci{
5818c2ecf20Sopenharmony_ci	its_mask_encode(&cmd->raw_cmd[0], prio >> 4, 23, 20);
5828c2ecf20Sopenharmony_ci}
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_cistatic void its_encode_sgi_group(struct its_cmd_block *cmd, bool grp)
5858c2ecf20Sopenharmony_ci{
5868c2ecf20Sopenharmony_ci	its_mask_encode(&cmd->raw_cmd[0], grp, 10, 10);
5878c2ecf20Sopenharmony_ci}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_cistatic void its_encode_sgi_clear(struct its_cmd_block *cmd, bool clr)
5908c2ecf20Sopenharmony_ci{
5918c2ecf20Sopenharmony_ci	its_mask_encode(&cmd->raw_cmd[0], clr, 9, 9);
5928c2ecf20Sopenharmony_ci}
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_cistatic void its_encode_sgi_enable(struct its_cmd_block *cmd, bool en)
5958c2ecf20Sopenharmony_ci{
5968c2ecf20Sopenharmony_ci	its_mask_encode(&cmd->raw_cmd[0], en, 8, 8);
5978c2ecf20Sopenharmony_ci}
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_cistatic inline void its_fixup_cmd(struct its_cmd_block *cmd)
6008c2ecf20Sopenharmony_ci{
6018c2ecf20Sopenharmony_ci	/* Let's fixup BE commands */
6028c2ecf20Sopenharmony_ci	cmd->raw_cmd_le[0] = cpu_to_le64(cmd->raw_cmd[0]);
6038c2ecf20Sopenharmony_ci	cmd->raw_cmd_le[1] = cpu_to_le64(cmd->raw_cmd[1]);
6048c2ecf20Sopenharmony_ci	cmd->raw_cmd_le[2] = cpu_to_le64(cmd->raw_cmd[2]);
6058c2ecf20Sopenharmony_ci	cmd->raw_cmd_le[3] = cpu_to_le64(cmd->raw_cmd[3]);
6068c2ecf20Sopenharmony_ci}
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_cistatic struct its_collection *its_build_mapd_cmd(struct its_node *its,
6098c2ecf20Sopenharmony_ci						 struct its_cmd_block *cmd,
6108c2ecf20Sopenharmony_ci						 struct its_cmd_desc *desc)
6118c2ecf20Sopenharmony_ci{
6128c2ecf20Sopenharmony_ci	unsigned long itt_addr;
6138c2ecf20Sopenharmony_ci	u8 size = ilog2(desc->its_mapd_cmd.dev->nr_ites);
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	itt_addr = virt_to_phys(desc->its_mapd_cmd.dev->itt);
6168c2ecf20Sopenharmony_ci	itt_addr = ALIGN(itt_addr, ITS_ITT_ALIGN);
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	its_encode_cmd(cmd, GITS_CMD_MAPD);
6198c2ecf20Sopenharmony_ci	its_encode_devid(cmd, desc->its_mapd_cmd.dev->device_id);
6208c2ecf20Sopenharmony_ci	its_encode_size(cmd, size - 1);
6218c2ecf20Sopenharmony_ci	its_encode_itt(cmd, itt_addr);
6228c2ecf20Sopenharmony_ci	its_encode_valid(cmd, desc->its_mapd_cmd.valid);
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	its_fixup_cmd(cmd);
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	return NULL;
6278c2ecf20Sopenharmony_ci}
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_cistatic struct its_collection *its_build_mapc_cmd(struct its_node *its,
6308c2ecf20Sopenharmony_ci						 struct its_cmd_block *cmd,
6318c2ecf20Sopenharmony_ci						 struct its_cmd_desc *desc)
6328c2ecf20Sopenharmony_ci{
6338c2ecf20Sopenharmony_ci	its_encode_cmd(cmd, GITS_CMD_MAPC);
6348c2ecf20Sopenharmony_ci	its_encode_collection(cmd, desc->its_mapc_cmd.col->col_id);
6358c2ecf20Sopenharmony_ci	its_encode_target(cmd, desc->its_mapc_cmd.col->target_address);
6368c2ecf20Sopenharmony_ci	its_encode_valid(cmd, desc->its_mapc_cmd.valid);
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	its_fixup_cmd(cmd);
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	return desc->its_mapc_cmd.col;
6418c2ecf20Sopenharmony_ci}
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_cistatic struct its_collection *its_build_mapti_cmd(struct its_node *its,
6448c2ecf20Sopenharmony_ci						  struct its_cmd_block *cmd,
6458c2ecf20Sopenharmony_ci						  struct its_cmd_desc *desc)
6468c2ecf20Sopenharmony_ci{
6478c2ecf20Sopenharmony_ci	struct its_collection *col;
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	col = dev_event_to_col(desc->its_mapti_cmd.dev,
6508c2ecf20Sopenharmony_ci			       desc->its_mapti_cmd.event_id);
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	its_encode_cmd(cmd, GITS_CMD_MAPTI);
6538c2ecf20Sopenharmony_ci	its_encode_devid(cmd, desc->its_mapti_cmd.dev->device_id);
6548c2ecf20Sopenharmony_ci	its_encode_event_id(cmd, desc->its_mapti_cmd.event_id);
6558c2ecf20Sopenharmony_ci	its_encode_phys_id(cmd, desc->its_mapti_cmd.phys_id);
6568c2ecf20Sopenharmony_ci	its_encode_collection(cmd, col->col_id);
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	its_fixup_cmd(cmd);
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	return valid_col(col);
6618c2ecf20Sopenharmony_ci}
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_cistatic struct its_collection *its_build_movi_cmd(struct its_node *its,
6648c2ecf20Sopenharmony_ci						 struct its_cmd_block *cmd,
6658c2ecf20Sopenharmony_ci						 struct its_cmd_desc *desc)
6668c2ecf20Sopenharmony_ci{
6678c2ecf20Sopenharmony_ci	struct its_collection *col;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	col = dev_event_to_col(desc->its_movi_cmd.dev,
6708c2ecf20Sopenharmony_ci			       desc->its_movi_cmd.event_id);
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	its_encode_cmd(cmd, GITS_CMD_MOVI);
6738c2ecf20Sopenharmony_ci	its_encode_devid(cmd, desc->its_movi_cmd.dev->device_id);
6748c2ecf20Sopenharmony_ci	its_encode_event_id(cmd, desc->its_movi_cmd.event_id);
6758c2ecf20Sopenharmony_ci	its_encode_collection(cmd, desc->its_movi_cmd.col->col_id);
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	its_fixup_cmd(cmd);
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	return valid_col(col);
6808c2ecf20Sopenharmony_ci}
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_cistatic struct its_collection *its_build_discard_cmd(struct its_node *its,
6838c2ecf20Sopenharmony_ci						    struct its_cmd_block *cmd,
6848c2ecf20Sopenharmony_ci						    struct its_cmd_desc *desc)
6858c2ecf20Sopenharmony_ci{
6868c2ecf20Sopenharmony_ci	struct its_collection *col;
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	col = dev_event_to_col(desc->its_discard_cmd.dev,
6898c2ecf20Sopenharmony_ci			       desc->its_discard_cmd.event_id);
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	its_encode_cmd(cmd, GITS_CMD_DISCARD);
6928c2ecf20Sopenharmony_ci	its_encode_devid(cmd, desc->its_discard_cmd.dev->device_id);
6938c2ecf20Sopenharmony_ci	its_encode_event_id(cmd, desc->its_discard_cmd.event_id);
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	its_fixup_cmd(cmd);
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	return valid_col(col);
6988c2ecf20Sopenharmony_ci}
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_cistatic struct its_collection *its_build_inv_cmd(struct its_node *its,
7018c2ecf20Sopenharmony_ci						struct its_cmd_block *cmd,
7028c2ecf20Sopenharmony_ci						struct its_cmd_desc *desc)
7038c2ecf20Sopenharmony_ci{
7048c2ecf20Sopenharmony_ci	struct its_collection *col;
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	col = dev_event_to_col(desc->its_inv_cmd.dev,
7078c2ecf20Sopenharmony_ci			       desc->its_inv_cmd.event_id);
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	its_encode_cmd(cmd, GITS_CMD_INV);
7108c2ecf20Sopenharmony_ci	its_encode_devid(cmd, desc->its_inv_cmd.dev->device_id);
7118c2ecf20Sopenharmony_ci	its_encode_event_id(cmd, desc->its_inv_cmd.event_id);
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	its_fixup_cmd(cmd);
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	return valid_col(col);
7168c2ecf20Sopenharmony_ci}
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_cistatic struct its_collection *its_build_int_cmd(struct its_node *its,
7198c2ecf20Sopenharmony_ci						struct its_cmd_block *cmd,
7208c2ecf20Sopenharmony_ci						struct its_cmd_desc *desc)
7218c2ecf20Sopenharmony_ci{
7228c2ecf20Sopenharmony_ci	struct its_collection *col;
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	col = dev_event_to_col(desc->its_int_cmd.dev,
7258c2ecf20Sopenharmony_ci			       desc->its_int_cmd.event_id);
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	its_encode_cmd(cmd, GITS_CMD_INT);
7288c2ecf20Sopenharmony_ci	its_encode_devid(cmd, desc->its_int_cmd.dev->device_id);
7298c2ecf20Sopenharmony_ci	its_encode_event_id(cmd, desc->its_int_cmd.event_id);
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	its_fixup_cmd(cmd);
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	return valid_col(col);
7348c2ecf20Sopenharmony_ci}
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_cistatic struct its_collection *its_build_clear_cmd(struct its_node *its,
7378c2ecf20Sopenharmony_ci						  struct its_cmd_block *cmd,
7388c2ecf20Sopenharmony_ci						  struct its_cmd_desc *desc)
7398c2ecf20Sopenharmony_ci{
7408c2ecf20Sopenharmony_ci	struct its_collection *col;
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	col = dev_event_to_col(desc->its_clear_cmd.dev,
7438c2ecf20Sopenharmony_ci			       desc->its_clear_cmd.event_id);
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	its_encode_cmd(cmd, GITS_CMD_CLEAR);
7468c2ecf20Sopenharmony_ci	its_encode_devid(cmd, desc->its_clear_cmd.dev->device_id);
7478c2ecf20Sopenharmony_ci	its_encode_event_id(cmd, desc->its_clear_cmd.event_id);
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	its_fixup_cmd(cmd);
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	return valid_col(col);
7528c2ecf20Sopenharmony_ci}
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_cistatic struct its_collection *its_build_invall_cmd(struct its_node *its,
7558c2ecf20Sopenharmony_ci						   struct its_cmd_block *cmd,
7568c2ecf20Sopenharmony_ci						   struct its_cmd_desc *desc)
7578c2ecf20Sopenharmony_ci{
7588c2ecf20Sopenharmony_ci	its_encode_cmd(cmd, GITS_CMD_INVALL);
7598c2ecf20Sopenharmony_ci	its_encode_collection(cmd, desc->its_invall_cmd.col->col_id);
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	its_fixup_cmd(cmd);
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	return desc->its_invall_cmd.col;
7648c2ecf20Sopenharmony_ci}
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_cistatic struct its_vpe *its_build_vinvall_cmd(struct its_node *its,
7678c2ecf20Sopenharmony_ci					     struct its_cmd_block *cmd,
7688c2ecf20Sopenharmony_ci					     struct its_cmd_desc *desc)
7698c2ecf20Sopenharmony_ci{
7708c2ecf20Sopenharmony_ci	its_encode_cmd(cmd, GITS_CMD_VINVALL);
7718c2ecf20Sopenharmony_ci	its_encode_vpeid(cmd, desc->its_vinvall_cmd.vpe->vpe_id);
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	its_fixup_cmd(cmd);
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	return valid_vpe(its, desc->its_vinvall_cmd.vpe);
7768c2ecf20Sopenharmony_ci}
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_cistatic struct its_vpe *its_build_vmapp_cmd(struct its_node *its,
7798c2ecf20Sopenharmony_ci					   struct its_cmd_block *cmd,
7808c2ecf20Sopenharmony_ci					   struct its_cmd_desc *desc)
7818c2ecf20Sopenharmony_ci{
7828c2ecf20Sopenharmony_ci	unsigned long vpt_addr, vconf_addr;
7838c2ecf20Sopenharmony_ci	u64 target;
7848c2ecf20Sopenharmony_ci	bool alloc;
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	its_encode_cmd(cmd, GITS_CMD_VMAPP);
7878c2ecf20Sopenharmony_ci	its_encode_vpeid(cmd, desc->its_vmapp_cmd.vpe->vpe_id);
7888c2ecf20Sopenharmony_ci	its_encode_valid(cmd, desc->its_vmapp_cmd.valid);
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	if (!desc->its_vmapp_cmd.valid) {
7918c2ecf20Sopenharmony_ci		if (is_v4_1(its)) {
7928c2ecf20Sopenharmony_ci			alloc = !atomic_dec_return(&desc->its_vmapp_cmd.vpe->vmapp_count);
7938c2ecf20Sopenharmony_ci			its_encode_alloc(cmd, alloc);
7948c2ecf20Sopenharmony_ci		}
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci		goto out;
7978c2ecf20Sopenharmony_ci	}
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	vpt_addr = virt_to_phys(page_address(desc->its_vmapp_cmd.vpe->vpt_page));
8008c2ecf20Sopenharmony_ci	target = desc->its_vmapp_cmd.col->target_address + its->vlpi_redist_offset;
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	its_encode_target(cmd, target);
8038c2ecf20Sopenharmony_ci	its_encode_vpt_addr(cmd, vpt_addr);
8048c2ecf20Sopenharmony_ci	its_encode_vpt_size(cmd, LPI_NRBITS - 1);
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	if (!is_v4_1(its))
8078c2ecf20Sopenharmony_ci		goto out;
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	vconf_addr = virt_to_phys(page_address(desc->its_vmapp_cmd.vpe->its_vm->vprop_page));
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	alloc = !atomic_fetch_inc(&desc->its_vmapp_cmd.vpe->vmapp_count);
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	its_encode_alloc(cmd, alloc);
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci	/* We can only signal PTZ when alloc==1. Why do we have two bits? */
8168c2ecf20Sopenharmony_ci	its_encode_ptz(cmd, alloc);
8178c2ecf20Sopenharmony_ci	its_encode_vconf_addr(cmd, vconf_addr);
8188c2ecf20Sopenharmony_ci	its_encode_vmapp_default_db(cmd, desc->its_vmapp_cmd.vpe->vpe_db_lpi);
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ciout:
8218c2ecf20Sopenharmony_ci	its_fixup_cmd(cmd);
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	return valid_vpe(its, desc->its_vmapp_cmd.vpe);
8248c2ecf20Sopenharmony_ci}
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_cistatic struct its_vpe *its_build_vmapti_cmd(struct its_node *its,
8278c2ecf20Sopenharmony_ci					    struct its_cmd_block *cmd,
8288c2ecf20Sopenharmony_ci					    struct its_cmd_desc *desc)
8298c2ecf20Sopenharmony_ci{
8308c2ecf20Sopenharmony_ci	u32 db;
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	if (!is_v4_1(its) && desc->its_vmapti_cmd.db_enabled)
8338c2ecf20Sopenharmony_ci		db = desc->its_vmapti_cmd.vpe->vpe_db_lpi;
8348c2ecf20Sopenharmony_ci	else
8358c2ecf20Sopenharmony_ci		db = 1023;
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	its_encode_cmd(cmd, GITS_CMD_VMAPTI);
8388c2ecf20Sopenharmony_ci	its_encode_devid(cmd, desc->its_vmapti_cmd.dev->device_id);
8398c2ecf20Sopenharmony_ci	its_encode_vpeid(cmd, desc->its_vmapti_cmd.vpe->vpe_id);
8408c2ecf20Sopenharmony_ci	its_encode_event_id(cmd, desc->its_vmapti_cmd.event_id);
8418c2ecf20Sopenharmony_ci	its_encode_db_phys_id(cmd, db);
8428c2ecf20Sopenharmony_ci	its_encode_virt_id(cmd, desc->its_vmapti_cmd.virt_id);
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	its_fixup_cmd(cmd);
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	return valid_vpe(its, desc->its_vmapti_cmd.vpe);
8478c2ecf20Sopenharmony_ci}
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_cistatic struct its_vpe *its_build_vmovi_cmd(struct its_node *its,
8508c2ecf20Sopenharmony_ci					   struct its_cmd_block *cmd,
8518c2ecf20Sopenharmony_ci					   struct its_cmd_desc *desc)
8528c2ecf20Sopenharmony_ci{
8538c2ecf20Sopenharmony_ci	u32 db;
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	if (!is_v4_1(its) && desc->its_vmovi_cmd.db_enabled)
8568c2ecf20Sopenharmony_ci		db = desc->its_vmovi_cmd.vpe->vpe_db_lpi;
8578c2ecf20Sopenharmony_ci	else
8588c2ecf20Sopenharmony_ci		db = 1023;
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci	its_encode_cmd(cmd, GITS_CMD_VMOVI);
8618c2ecf20Sopenharmony_ci	its_encode_devid(cmd, desc->its_vmovi_cmd.dev->device_id);
8628c2ecf20Sopenharmony_ci	its_encode_vpeid(cmd, desc->its_vmovi_cmd.vpe->vpe_id);
8638c2ecf20Sopenharmony_ci	its_encode_event_id(cmd, desc->its_vmovi_cmd.event_id);
8648c2ecf20Sopenharmony_ci	its_encode_db_phys_id(cmd, db);
8658c2ecf20Sopenharmony_ci	its_encode_db_valid(cmd, true);
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	its_fixup_cmd(cmd);
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	return valid_vpe(its, desc->its_vmovi_cmd.vpe);
8708c2ecf20Sopenharmony_ci}
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_cistatic struct its_vpe *its_build_vmovp_cmd(struct its_node *its,
8738c2ecf20Sopenharmony_ci					   struct its_cmd_block *cmd,
8748c2ecf20Sopenharmony_ci					   struct its_cmd_desc *desc)
8758c2ecf20Sopenharmony_ci{
8768c2ecf20Sopenharmony_ci	u64 target;
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	target = desc->its_vmovp_cmd.col->target_address + its->vlpi_redist_offset;
8798c2ecf20Sopenharmony_ci	its_encode_cmd(cmd, GITS_CMD_VMOVP);
8808c2ecf20Sopenharmony_ci	its_encode_seq_num(cmd, desc->its_vmovp_cmd.seq_num);
8818c2ecf20Sopenharmony_ci	its_encode_its_list(cmd, desc->its_vmovp_cmd.its_list);
8828c2ecf20Sopenharmony_ci	its_encode_vpeid(cmd, desc->its_vmovp_cmd.vpe->vpe_id);
8838c2ecf20Sopenharmony_ci	its_encode_target(cmd, target);
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci	if (is_v4_1(its)) {
8868c2ecf20Sopenharmony_ci		its_encode_db(cmd, true);
8878c2ecf20Sopenharmony_ci		its_encode_vmovp_default_db(cmd, desc->its_vmovp_cmd.vpe->vpe_db_lpi);
8888c2ecf20Sopenharmony_ci	}
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	its_fixup_cmd(cmd);
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	return valid_vpe(its, desc->its_vmovp_cmd.vpe);
8938c2ecf20Sopenharmony_ci}
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_cistatic struct its_vpe *its_build_vinv_cmd(struct its_node *its,
8968c2ecf20Sopenharmony_ci					  struct its_cmd_block *cmd,
8978c2ecf20Sopenharmony_ci					  struct its_cmd_desc *desc)
8988c2ecf20Sopenharmony_ci{
8998c2ecf20Sopenharmony_ci	struct its_vlpi_map *map;
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	map = dev_event_to_vlpi_map(desc->its_inv_cmd.dev,
9028c2ecf20Sopenharmony_ci				    desc->its_inv_cmd.event_id);
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	its_encode_cmd(cmd, GITS_CMD_INV);
9058c2ecf20Sopenharmony_ci	its_encode_devid(cmd, desc->its_inv_cmd.dev->device_id);
9068c2ecf20Sopenharmony_ci	its_encode_event_id(cmd, desc->its_inv_cmd.event_id);
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	its_fixup_cmd(cmd);
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci	return valid_vpe(its, map->vpe);
9118c2ecf20Sopenharmony_ci}
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_cistatic struct its_vpe *its_build_vint_cmd(struct its_node *its,
9148c2ecf20Sopenharmony_ci					  struct its_cmd_block *cmd,
9158c2ecf20Sopenharmony_ci					  struct its_cmd_desc *desc)
9168c2ecf20Sopenharmony_ci{
9178c2ecf20Sopenharmony_ci	struct its_vlpi_map *map;
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	map = dev_event_to_vlpi_map(desc->its_int_cmd.dev,
9208c2ecf20Sopenharmony_ci				    desc->its_int_cmd.event_id);
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	its_encode_cmd(cmd, GITS_CMD_INT);
9238c2ecf20Sopenharmony_ci	its_encode_devid(cmd, desc->its_int_cmd.dev->device_id);
9248c2ecf20Sopenharmony_ci	its_encode_event_id(cmd, desc->its_int_cmd.event_id);
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci	its_fixup_cmd(cmd);
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci	return valid_vpe(its, map->vpe);
9298c2ecf20Sopenharmony_ci}
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_cistatic struct its_vpe *its_build_vclear_cmd(struct its_node *its,
9328c2ecf20Sopenharmony_ci					    struct its_cmd_block *cmd,
9338c2ecf20Sopenharmony_ci					    struct its_cmd_desc *desc)
9348c2ecf20Sopenharmony_ci{
9358c2ecf20Sopenharmony_ci	struct its_vlpi_map *map;
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci	map = dev_event_to_vlpi_map(desc->its_clear_cmd.dev,
9388c2ecf20Sopenharmony_ci				    desc->its_clear_cmd.event_id);
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	its_encode_cmd(cmd, GITS_CMD_CLEAR);
9418c2ecf20Sopenharmony_ci	its_encode_devid(cmd, desc->its_clear_cmd.dev->device_id);
9428c2ecf20Sopenharmony_ci	its_encode_event_id(cmd, desc->its_clear_cmd.event_id);
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci	its_fixup_cmd(cmd);
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	return valid_vpe(its, map->vpe);
9478c2ecf20Sopenharmony_ci}
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_cistatic struct its_vpe *its_build_invdb_cmd(struct its_node *its,
9508c2ecf20Sopenharmony_ci					   struct its_cmd_block *cmd,
9518c2ecf20Sopenharmony_ci					   struct its_cmd_desc *desc)
9528c2ecf20Sopenharmony_ci{
9538c2ecf20Sopenharmony_ci	if (WARN_ON(!is_v4_1(its)))
9548c2ecf20Sopenharmony_ci		return NULL;
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci	its_encode_cmd(cmd, GITS_CMD_INVDB);
9578c2ecf20Sopenharmony_ci	its_encode_vpeid(cmd, desc->its_invdb_cmd.vpe->vpe_id);
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci	its_fixup_cmd(cmd);
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci	return valid_vpe(its, desc->its_invdb_cmd.vpe);
9628c2ecf20Sopenharmony_ci}
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_cistatic struct its_vpe *its_build_vsgi_cmd(struct its_node *its,
9658c2ecf20Sopenharmony_ci					  struct its_cmd_block *cmd,
9668c2ecf20Sopenharmony_ci					  struct its_cmd_desc *desc)
9678c2ecf20Sopenharmony_ci{
9688c2ecf20Sopenharmony_ci	if (WARN_ON(!is_v4_1(its)))
9698c2ecf20Sopenharmony_ci		return NULL;
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci	its_encode_cmd(cmd, GITS_CMD_VSGI);
9728c2ecf20Sopenharmony_ci	its_encode_vpeid(cmd, desc->its_vsgi_cmd.vpe->vpe_id);
9738c2ecf20Sopenharmony_ci	its_encode_sgi_intid(cmd, desc->its_vsgi_cmd.sgi);
9748c2ecf20Sopenharmony_ci	its_encode_sgi_priority(cmd, desc->its_vsgi_cmd.priority);
9758c2ecf20Sopenharmony_ci	its_encode_sgi_group(cmd, desc->its_vsgi_cmd.group);
9768c2ecf20Sopenharmony_ci	its_encode_sgi_clear(cmd, desc->its_vsgi_cmd.clear);
9778c2ecf20Sopenharmony_ci	its_encode_sgi_enable(cmd, desc->its_vsgi_cmd.enable);
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci	its_fixup_cmd(cmd);
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	return valid_vpe(its, desc->its_vsgi_cmd.vpe);
9828c2ecf20Sopenharmony_ci}
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_cistatic u64 its_cmd_ptr_to_offset(struct its_node *its,
9858c2ecf20Sopenharmony_ci				 struct its_cmd_block *ptr)
9868c2ecf20Sopenharmony_ci{
9878c2ecf20Sopenharmony_ci	return (ptr - its->cmd_base) * sizeof(*ptr);
9888c2ecf20Sopenharmony_ci}
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_cistatic int its_queue_full(struct its_node *its)
9918c2ecf20Sopenharmony_ci{
9928c2ecf20Sopenharmony_ci	int widx;
9938c2ecf20Sopenharmony_ci	int ridx;
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci	widx = its->cmd_write - its->cmd_base;
9968c2ecf20Sopenharmony_ci	ridx = readl_relaxed(its->base + GITS_CREADR) / sizeof(struct its_cmd_block);
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci	/* This is incredibly unlikely to happen, unless the ITS locks up. */
9998c2ecf20Sopenharmony_ci	if (((widx + 1) % ITS_CMD_QUEUE_NR_ENTRIES) == ridx)
10008c2ecf20Sopenharmony_ci		return 1;
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci	return 0;
10038c2ecf20Sopenharmony_ci}
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_cistatic struct its_cmd_block *its_allocate_entry(struct its_node *its)
10068c2ecf20Sopenharmony_ci{
10078c2ecf20Sopenharmony_ci	struct its_cmd_block *cmd;
10088c2ecf20Sopenharmony_ci	u32 count = 1000000;	/* 1s! */
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	while (its_queue_full(its)) {
10118c2ecf20Sopenharmony_ci		count--;
10128c2ecf20Sopenharmony_ci		if (!count) {
10138c2ecf20Sopenharmony_ci			pr_err_ratelimited("ITS queue not draining\n");
10148c2ecf20Sopenharmony_ci			return NULL;
10158c2ecf20Sopenharmony_ci		}
10168c2ecf20Sopenharmony_ci		cpu_relax();
10178c2ecf20Sopenharmony_ci		udelay(1);
10188c2ecf20Sopenharmony_ci	}
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci	cmd = its->cmd_write++;
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci	/* Handle queue wrapping */
10238c2ecf20Sopenharmony_ci	if (its->cmd_write == (its->cmd_base + ITS_CMD_QUEUE_NR_ENTRIES))
10248c2ecf20Sopenharmony_ci		its->cmd_write = its->cmd_base;
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_ci	/* Clear command  */
10278c2ecf20Sopenharmony_ci	cmd->raw_cmd[0] = 0;
10288c2ecf20Sopenharmony_ci	cmd->raw_cmd[1] = 0;
10298c2ecf20Sopenharmony_ci	cmd->raw_cmd[2] = 0;
10308c2ecf20Sopenharmony_ci	cmd->raw_cmd[3] = 0;
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_ci	return cmd;
10338c2ecf20Sopenharmony_ci}
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_cistatic struct its_cmd_block *its_post_commands(struct its_node *its)
10368c2ecf20Sopenharmony_ci{
10378c2ecf20Sopenharmony_ci	u64 wr = its_cmd_ptr_to_offset(its, its->cmd_write);
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci	writel_relaxed(wr, its->base + GITS_CWRITER);
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci	return its->cmd_write;
10428c2ecf20Sopenharmony_ci}
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_cistatic void its_flush_cmd(struct its_node *its, struct its_cmd_block *cmd)
10458c2ecf20Sopenharmony_ci{
10468c2ecf20Sopenharmony_ci	/*
10478c2ecf20Sopenharmony_ci	 * Make sure the commands written to memory are observable by
10488c2ecf20Sopenharmony_ci	 * the ITS.
10498c2ecf20Sopenharmony_ci	 */
10508c2ecf20Sopenharmony_ci	if (its->flags & ITS_FLAGS_CMDQ_NEEDS_FLUSHING)
10518c2ecf20Sopenharmony_ci		gic_flush_dcache_to_poc(cmd, sizeof(*cmd));
10528c2ecf20Sopenharmony_ci	else
10538c2ecf20Sopenharmony_ci		dsb(ishst);
10548c2ecf20Sopenharmony_ci}
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_cistatic int its_wait_for_range_completion(struct its_node *its,
10578c2ecf20Sopenharmony_ci					 u64	prev_idx,
10588c2ecf20Sopenharmony_ci					 struct its_cmd_block *to)
10598c2ecf20Sopenharmony_ci{
10608c2ecf20Sopenharmony_ci	u64 rd_idx, to_idx, linear_idx;
10618c2ecf20Sopenharmony_ci	u32 count = 1000000;	/* 1s! */
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_ci	/* Linearize to_idx if the command set has wrapped around */
10648c2ecf20Sopenharmony_ci	to_idx = its_cmd_ptr_to_offset(its, to);
10658c2ecf20Sopenharmony_ci	if (to_idx < prev_idx)
10668c2ecf20Sopenharmony_ci		to_idx += ITS_CMD_QUEUE_SZ;
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_ci	linear_idx = prev_idx;
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci	while (1) {
10718c2ecf20Sopenharmony_ci		s64 delta;
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci		rd_idx = readl_relaxed(its->base + GITS_CREADR);
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci		/*
10768c2ecf20Sopenharmony_ci		 * Compute the read pointer progress, taking the
10778c2ecf20Sopenharmony_ci		 * potential wrap-around into account.
10788c2ecf20Sopenharmony_ci		 */
10798c2ecf20Sopenharmony_ci		delta = rd_idx - prev_idx;
10808c2ecf20Sopenharmony_ci		if (rd_idx < prev_idx)
10818c2ecf20Sopenharmony_ci			delta += ITS_CMD_QUEUE_SZ;
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci		linear_idx += delta;
10848c2ecf20Sopenharmony_ci		if (linear_idx >= to_idx)
10858c2ecf20Sopenharmony_ci			break;
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci		count--;
10888c2ecf20Sopenharmony_ci		if (!count) {
10898c2ecf20Sopenharmony_ci			pr_err_ratelimited("ITS queue timeout (%llu %llu)\n",
10908c2ecf20Sopenharmony_ci					   to_idx, linear_idx);
10918c2ecf20Sopenharmony_ci			return -1;
10928c2ecf20Sopenharmony_ci		}
10938c2ecf20Sopenharmony_ci		prev_idx = rd_idx;
10948c2ecf20Sopenharmony_ci		cpu_relax();
10958c2ecf20Sopenharmony_ci		udelay(1);
10968c2ecf20Sopenharmony_ci	}
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_ci	return 0;
10998c2ecf20Sopenharmony_ci}
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci/* Warning, macro hell follows */
11028c2ecf20Sopenharmony_ci#define BUILD_SINGLE_CMD_FUNC(name, buildtype, synctype, buildfn)	\
11038c2ecf20Sopenharmony_civoid name(struct its_node *its,						\
11048c2ecf20Sopenharmony_ci	  buildtype builder,						\
11058c2ecf20Sopenharmony_ci	  struct its_cmd_desc *desc)					\
11068c2ecf20Sopenharmony_ci{									\
11078c2ecf20Sopenharmony_ci	struct its_cmd_block *cmd, *sync_cmd, *next_cmd;		\
11088c2ecf20Sopenharmony_ci	synctype *sync_obj;						\
11098c2ecf20Sopenharmony_ci	unsigned long flags;						\
11108c2ecf20Sopenharmony_ci	u64 rd_idx;							\
11118c2ecf20Sopenharmony_ci									\
11128c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&its->lock, flags);			\
11138c2ecf20Sopenharmony_ci									\
11148c2ecf20Sopenharmony_ci	cmd = its_allocate_entry(its);					\
11158c2ecf20Sopenharmony_ci	if (!cmd) {		/* We're soooooo screewed... */		\
11168c2ecf20Sopenharmony_ci		raw_spin_unlock_irqrestore(&its->lock, flags);		\
11178c2ecf20Sopenharmony_ci		return;							\
11188c2ecf20Sopenharmony_ci	}								\
11198c2ecf20Sopenharmony_ci	sync_obj = builder(its, cmd, desc);				\
11208c2ecf20Sopenharmony_ci	its_flush_cmd(its, cmd);					\
11218c2ecf20Sopenharmony_ci									\
11228c2ecf20Sopenharmony_ci	if (sync_obj) {							\
11238c2ecf20Sopenharmony_ci		sync_cmd = its_allocate_entry(its);			\
11248c2ecf20Sopenharmony_ci		if (!sync_cmd)						\
11258c2ecf20Sopenharmony_ci			goto post;					\
11268c2ecf20Sopenharmony_ci									\
11278c2ecf20Sopenharmony_ci		buildfn(its, sync_cmd, sync_obj);			\
11288c2ecf20Sopenharmony_ci		its_flush_cmd(its, sync_cmd);				\
11298c2ecf20Sopenharmony_ci	}								\
11308c2ecf20Sopenharmony_ci									\
11318c2ecf20Sopenharmony_cipost:									\
11328c2ecf20Sopenharmony_ci	rd_idx = readl_relaxed(its->base + GITS_CREADR);		\
11338c2ecf20Sopenharmony_ci	next_cmd = its_post_commands(its);				\
11348c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&its->lock, flags);			\
11358c2ecf20Sopenharmony_ci									\
11368c2ecf20Sopenharmony_ci	if (its_wait_for_range_completion(its, rd_idx, next_cmd))	\
11378c2ecf20Sopenharmony_ci		pr_err_ratelimited("ITS cmd %ps failed\n", builder);	\
11388c2ecf20Sopenharmony_ci}
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_cistatic void its_build_sync_cmd(struct its_node *its,
11418c2ecf20Sopenharmony_ci			       struct its_cmd_block *sync_cmd,
11428c2ecf20Sopenharmony_ci			       struct its_collection *sync_col)
11438c2ecf20Sopenharmony_ci{
11448c2ecf20Sopenharmony_ci	its_encode_cmd(sync_cmd, GITS_CMD_SYNC);
11458c2ecf20Sopenharmony_ci	its_encode_target(sync_cmd, sync_col->target_address);
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ci	its_fixup_cmd(sync_cmd);
11488c2ecf20Sopenharmony_ci}
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_cistatic BUILD_SINGLE_CMD_FUNC(its_send_single_command, its_cmd_builder_t,
11518c2ecf20Sopenharmony_ci			     struct its_collection, its_build_sync_cmd)
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_cistatic void its_build_vsync_cmd(struct its_node *its,
11548c2ecf20Sopenharmony_ci				struct its_cmd_block *sync_cmd,
11558c2ecf20Sopenharmony_ci				struct its_vpe *sync_vpe)
11568c2ecf20Sopenharmony_ci{
11578c2ecf20Sopenharmony_ci	its_encode_cmd(sync_cmd, GITS_CMD_VSYNC);
11588c2ecf20Sopenharmony_ci	its_encode_vpeid(sync_cmd, sync_vpe->vpe_id);
11598c2ecf20Sopenharmony_ci
11608c2ecf20Sopenharmony_ci	its_fixup_cmd(sync_cmd);
11618c2ecf20Sopenharmony_ci}
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_cistatic BUILD_SINGLE_CMD_FUNC(its_send_single_vcommand, its_cmd_vbuilder_t,
11648c2ecf20Sopenharmony_ci			     struct its_vpe, its_build_vsync_cmd)
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_cistatic void its_send_int(struct its_device *dev, u32 event_id)
11678c2ecf20Sopenharmony_ci{
11688c2ecf20Sopenharmony_ci	struct its_cmd_desc desc;
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci	desc.its_int_cmd.dev = dev;
11718c2ecf20Sopenharmony_ci	desc.its_int_cmd.event_id = event_id;
11728c2ecf20Sopenharmony_ci
11738c2ecf20Sopenharmony_ci	its_send_single_command(dev->its, its_build_int_cmd, &desc);
11748c2ecf20Sopenharmony_ci}
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_cistatic void its_send_clear(struct its_device *dev, u32 event_id)
11778c2ecf20Sopenharmony_ci{
11788c2ecf20Sopenharmony_ci	struct its_cmd_desc desc;
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci	desc.its_clear_cmd.dev = dev;
11818c2ecf20Sopenharmony_ci	desc.its_clear_cmd.event_id = event_id;
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci	its_send_single_command(dev->its, its_build_clear_cmd, &desc);
11848c2ecf20Sopenharmony_ci}
11858c2ecf20Sopenharmony_ci
11868c2ecf20Sopenharmony_cistatic void its_send_inv(struct its_device *dev, u32 event_id)
11878c2ecf20Sopenharmony_ci{
11888c2ecf20Sopenharmony_ci	struct its_cmd_desc desc;
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_ci	desc.its_inv_cmd.dev = dev;
11918c2ecf20Sopenharmony_ci	desc.its_inv_cmd.event_id = event_id;
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci	its_send_single_command(dev->its, its_build_inv_cmd, &desc);
11948c2ecf20Sopenharmony_ci}
11958c2ecf20Sopenharmony_ci
11968c2ecf20Sopenharmony_cistatic void its_send_mapd(struct its_device *dev, int valid)
11978c2ecf20Sopenharmony_ci{
11988c2ecf20Sopenharmony_ci	struct its_cmd_desc desc;
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_ci	desc.its_mapd_cmd.dev = dev;
12018c2ecf20Sopenharmony_ci	desc.its_mapd_cmd.valid = !!valid;
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci	its_send_single_command(dev->its, its_build_mapd_cmd, &desc);
12048c2ecf20Sopenharmony_ci}
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_cistatic void its_send_mapc(struct its_node *its, struct its_collection *col,
12078c2ecf20Sopenharmony_ci			  int valid)
12088c2ecf20Sopenharmony_ci{
12098c2ecf20Sopenharmony_ci	struct its_cmd_desc desc;
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	desc.its_mapc_cmd.col = col;
12128c2ecf20Sopenharmony_ci	desc.its_mapc_cmd.valid = !!valid;
12138c2ecf20Sopenharmony_ci
12148c2ecf20Sopenharmony_ci	its_send_single_command(its, its_build_mapc_cmd, &desc);
12158c2ecf20Sopenharmony_ci}
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_cistatic void its_send_mapti(struct its_device *dev, u32 irq_id, u32 id)
12188c2ecf20Sopenharmony_ci{
12198c2ecf20Sopenharmony_ci	struct its_cmd_desc desc;
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_ci	desc.its_mapti_cmd.dev = dev;
12228c2ecf20Sopenharmony_ci	desc.its_mapti_cmd.phys_id = irq_id;
12238c2ecf20Sopenharmony_ci	desc.its_mapti_cmd.event_id = id;
12248c2ecf20Sopenharmony_ci
12258c2ecf20Sopenharmony_ci	its_send_single_command(dev->its, its_build_mapti_cmd, &desc);
12268c2ecf20Sopenharmony_ci}
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_cistatic void its_send_movi(struct its_device *dev,
12298c2ecf20Sopenharmony_ci			  struct its_collection *col, u32 id)
12308c2ecf20Sopenharmony_ci{
12318c2ecf20Sopenharmony_ci	struct its_cmd_desc desc;
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_ci	desc.its_movi_cmd.dev = dev;
12348c2ecf20Sopenharmony_ci	desc.its_movi_cmd.col = col;
12358c2ecf20Sopenharmony_ci	desc.its_movi_cmd.event_id = id;
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_ci	its_send_single_command(dev->its, its_build_movi_cmd, &desc);
12388c2ecf20Sopenharmony_ci}
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_cistatic void its_send_discard(struct its_device *dev, u32 id)
12418c2ecf20Sopenharmony_ci{
12428c2ecf20Sopenharmony_ci	struct its_cmd_desc desc;
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_ci	desc.its_discard_cmd.dev = dev;
12458c2ecf20Sopenharmony_ci	desc.its_discard_cmd.event_id = id;
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci	its_send_single_command(dev->its, its_build_discard_cmd, &desc);
12488c2ecf20Sopenharmony_ci}
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_cistatic void its_send_invall(struct its_node *its, struct its_collection *col)
12518c2ecf20Sopenharmony_ci{
12528c2ecf20Sopenharmony_ci	struct its_cmd_desc desc;
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_ci	desc.its_invall_cmd.col = col;
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci	its_send_single_command(its, its_build_invall_cmd, &desc);
12578c2ecf20Sopenharmony_ci}
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_cistatic void its_send_vmapti(struct its_device *dev, u32 id)
12608c2ecf20Sopenharmony_ci{
12618c2ecf20Sopenharmony_ci	struct its_vlpi_map *map = dev_event_to_vlpi_map(dev, id);
12628c2ecf20Sopenharmony_ci	struct its_cmd_desc desc;
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_ci	desc.its_vmapti_cmd.vpe = map->vpe;
12658c2ecf20Sopenharmony_ci	desc.its_vmapti_cmd.dev = dev;
12668c2ecf20Sopenharmony_ci	desc.its_vmapti_cmd.virt_id = map->vintid;
12678c2ecf20Sopenharmony_ci	desc.its_vmapti_cmd.event_id = id;
12688c2ecf20Sopenharmony_ci	desc.its_vmapti_cmd.db_enabled = map->db_enabled;
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_ci	its_send_single_vcommand(dev->its, its_build_vmapti_cmd, &desc);
12718c2ecf20Sopenharmony_ci}
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_cistatic void its_send_vmovi(struct its_device *dev, u32 id)
12748c2ecf20Sopenharmony_ci{
12758c2ecf20Sopenharmony_ci	struct its_vlpi_map *map = dev_event_to_vlpi_map(dev, id);
12768c2ecf20Sopenharmony_ci	struct its_cmd_desc desc;
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ci	desc.its_vmovi_cmd.vpe = map->vpe;
12798c2ecf20Sopenharmony_ci	desc.its_vmovi_cmd.dev = dev;
12808c2ecf20Sopenharmony_ci	desc.its_vmovi_cmd.event_id = id;
12818c2ecf20Sopenharmony_ci	desc.its_vmovi_cmd.db_enabled = map->db_enabled;
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci	its_send_single_vcommand(dev->its, its_build_vmovi_cmd, &desc);
12848c2ecf20Sopenharmony_ci}
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_cistatic void its_send_vmapp(struct its_node *its,
12878c2ecf20Sopenharmony_ci			   struct its_vpe *vpe, bool valid)
12888c2ecf20Sopenharmony_ci{
12898c2ecf20Sopenharmony_ci	struct its_cmd_desc desc;
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci	desc.its_vmapp_cmd.vpe = vpe;
12928c2ecf20Sopenharmony_ci	desc.its_vmapp_cmd.valid = valid;
12938c2ecf20Sopenharmony_ci	desc.its_vmapp_cmd.col = &its->collections[vpe->col_idx];
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci	its_send_single_vcommand(its, its_build_vmapp_cmd, &desc);
12968c2ecf20Sopenharmony_ci}
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_cistatic void its_send_vmovp(struct its_vpe *vpe)
12998c2ecf20Sopenharmony_ci{
13008c2ecf20Sopenharmony_ci	struct its_cmd_desc desc = {};
13018c2ecf20Sopenharmony_ci	struct its_node *its;
13028c2ecf20Sopenharmony_ci	unsigned long flags;
13038c2ecf20Sopenharmony_ci	int col_id = vpe->col_idx;
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ci	desc.its_vmovp_cmd.vpe = vpe;
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_ci	if (!its_list_map) {
13088c2ecf20Sopenharmony_ci		its = list_first_entry(&its_nodes, struct its_node, entry);
13098c2ecf20Sopenharmony_ci		desc.its_vmovp_cmd.col = &its->collections[col_id];
13108c2ecf20Sopenharmony_ci		its_send_single_vcommand(its, its_build_vmovp_cmd, &desc);
13118c2ecf20Sopenharmony_ci		return;
13128c2ecf20Sopenharmony_ci	}
13138c2ecf20Sopenharmony_ci
13148c2ecf20Sopenharmony_ci	/*
13158c2ecf20Sopenharmony_ci	 * Yet another marvel of the architecture. If using the
13168c2ecf20Sopenharmony_ci	 * its_list "feature", we need to make sure that all ITSs
13178c2ecf20Sopenharmony_ci	 * receive all VMOVP commands in the same order. The only way
13188c2ecf20Sopenharmony_ci	 * to guarantee this is to make vmovp a serialization point.
13198c2ecf20Sopenharmony_ci	 *
13208c2ecf20Sopenharmony_ci	 * Wall <-- Head.
13218c2ecf20Sopenharmony_ci	 */
13228c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&vmovp_lock, flags);
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_ci	desc.its_vmovp_cmd.seq_num = vmovp_seq_num++;
13258c2ecf20Sopenharmony_ci	desc.its_vmovp_cmd.its_list = get_its_list(vpe->its_vm);
13268c2ecf20Sopenharmony_ci
13278c2ecf20Sopenharmony_ci	/* Emit VMOVPs */
13288c2ecf20Sopenharmony_ci	list_for_each_entry(its, &its_nodes, entry) {
13298c2ecf20Sopenharmony_ci		if (!is_v4(its))
13308c2ecf20Sopenharmony_ci			continue;
13318c2ecf20Sopenharmony_ci
13328c2ecf20Sopenharmony_ci		if (!require_its_list_vmovp(vpe->its_vm, its))
13338c2ecf20Sopenharmony_ci			continue;
13348c2ecf20Sopenharmony_ci
13358c2ecf20Sopenharmony_ci		desc.its_vmovp_cmd.col = &its->collections[col_id];
13368c2ecf20Sopenharmony_ci		its_send_single_vcommand(its, its_build_vmovp_cmd, &desc);
13378c2ecf20Sopenharmony_ci	}
13388c2ecf20Sopenharmony_ci
13398c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&vmovp_lock, flags);
13408c2ecf20Sopenharmony_ci}
13418c2ecf20Sopenharmony_ci
13428c2ecf20Sopenharmony_cistatic void its_send_vinvall(struct its_node *its, struct its_vpe *vpe)
13438c2ecf20Sopenharmony_ci{
13448c2ecf20Sopenharmony_ci	struct its_cmd_desc desc;
13458c2ecf20Sopenharmony_ci
13468c2ecf20Sopenharmony_ci	desc.its_vinvall_cmd.vpe = vpe;
13478c2ecf20Sopenharmony_ci	its_send_single_vcommand(its, its_build_vinvall_cmd, &desc);
13488c2ecf20Sopenharmony_ci}
13498c2ecf20Sopenharmony_ci
13508c2ecf20Sopenharmony_cistatic void its_send_vinv(struct its_device *dev, u32 event_id)
13518c2ecf20Sopenharmony_ci{
13528c2ecf20Sopenharmony_ci	struct its_cmd_desc desc;
13538c2ecf20Sopenharmony_ci
13548c2ecf20Sopenharmony_ci	/*
13558c2ecf20Sopenharmony_ci	 * There is no real VINV command. This is just a normal INV,
13568c2ecf20Sopenharmony_ci	 * with a VSYNC instead of a SYNC.
13578c2ecf20Sopenharmony_ci	 */
13588c2ecf20Sopenharmony_ci	desc.its_inv_cmd.dev = dev;
13598c2ecf20Sopenharmony_ci	desc.its_inv_cmd.event_id = event_id;
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ci	its_send_single_vcommand(dev->its, its_build_vinv_cmd, &desc);
13628c2ecf20Sopenharmony_ci}
13638c2ecf20Sopenharmony_ci
13648c2ecf20Sopenharmony_cistatic void its_send_vint(struct its_device *dev, u32 event_id)
13658c2ecf20Sopenharmony_ci{
13668c2ecf20Sopenharmony_ci	struct its_cmd_desc desc;
13678c2ecf20Sopenharmony_ci
13688c2ecf20Sopenharmony_ci	/*
13698c2ecf20Sopenharmony_ci	 * There is no real VINT command. This is just a normal INT,
13708c2ecf20Sopenharmony_ci	 * with a VSYNC instead of a SYNC.
13718c2ecf20Sopenharmony_ci	 */
13728c2ecf20Sopenharmony_ci	desc.its_int_cmd.dev = dev;
13738c2ecf20Sopenharmony_ci	desc.its_int_cmd.event_id = event_id;
13748c2ecf20Sopenharmony_ci
13758c2ecf20Sopenharmony_ci	its_send_single_vcommand(dev->its, its_build_vint_cmd, &desc);
13768c2ecf20Sopenharmony_ci}
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_cistatic void its_send_vclear(struct its_device *dev, u32 event_id)
13798c2ecf20Sopenharmony_ci{
13808c2ecf20Sopenharmony_ci	struct its_cmd_desc desc;
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci	/*
13838c2ecf20Sopenharmony_ci	 * There is no real VCLEAR command. This is just a normal CLEAR,
13848c2ecf20Sopenharmony_ci	 * with a VSYNC instead of a SYNC.
13858c2ecf20Sopenharmony_ci	 */
13868c2ecf20Sopenharmony_ci	desc.its_clear_cmd.dev = dev;
13878c2ecf20Sopenharmony_ci	desc.its_clear_cmd.event_id = event_id;
13888c2ecf20Sopenharmony_ci
13898c2ecf20Sopenharmony_ci	its_send_single_vcommand(dev->its, its_build_vclear_cmd, &desc);
13908c2ecf20Sopenharmony_ci}
13918c2ecf20Sopenharmony_ci
13928c2ecf20Sopenharmony_cistatic void its_send_invdb(struct its_node *its, struct its_vpe *vpe)
13938c2ecf20Sopenharmony_ci{
13948c2ecf20Sopenharmony_ci	struct its_cmd_desc desc;
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci	desc.its_invdb_cmd.vpe = vpe;
13978c2ecf20Sopenharmony_ci	its_send_single_vcommand(its, its_build_invdb_cmd, &desc);
13988c2ecf20Sopenharmony_ci}
13998c2ecf20Sopenharmony_ci
14008c2ecf20Sopenharmony_ci/*
14018c2ecf20Sopenharmony_ci * irqchip functions - assumes MSI, mostly.
14028c2ecf20Sopenharmony_ci */
14038c2ecf20Sopenharmony_cistatic void lpi_write_config(struct irq_data *d, u8 clr, u8 set)
14048c2ecf20Sopenharmony_ci{
14058c2ecf20Sopenharmony_ci	struct its_vlpi_map *map = get_vlpi_map(d);
14068c2ecf20Sopenharmony_ci	irq_hw_number_t hwirq;
14078c2ecf20Sopenharmony_ci	void *va;
14088c2ecf20Sopenharmony_ci	u8 *cfg;
14098c2ecf20Sopenharmony_ci
14108c2ecf20Sopenharmony_ci	if (map) {
14118c2ecf20Sopenharmony_ci		va = page_address(map->vm->vprop_page);
14128c2ecf20Sopenharmony_ci		hwirq = map->vintid;
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_ci		/* Remember the updated property */
14158c2ecf20Sopenharmony_ci		map->properties &= ~clr;
14168c2ecf20Sopenharmony_ci		map->properties |= set | LPI_PROP_GROUP1;
14178c2ecf20Sopenharmony_ci	} else {
14188c2ecf20Sopenharmony_ci		va = gic_rdists->prop_table_va;
14198c2ecf20Sopenharmony_ci		hwirq = d->hwirq;
14208c2ecf20Sopenharmony_ci	}
14218c2ecf20Sopenharmony_ci
14228c2ecf20Sopenharmony_ci	cfg = va + hwirq - 8192;
14238c2ecf20Sopenharmony_ci	*cfg &= ~clr;
14248c2ecf20Sopenharmony_ci	*cfg |= set | LPI_PROP_GROUP1;
14258c2ecf20Sopenharmony_ci
14268c2ecf20Sopenharmony_ci	/*
14278c2ecf20Sopenharmony_ci	 * Make the above write visible to the redistributors.
14288c2ecf20Sopenharmony_ci	 * And yes, we're flushing exactly: One. Single. Byte.
14298c2ecf20Sopenharmony_ci	 * Humpf...
14308c2ecf20Sopenharmony_ci	 */
14318c2ecf20Sopenharmony_ci	if (gic_rdists->flags & RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING)
14328c2ecf20Sopenharmony_ci		gic_flush_dcache_to_poc(cfg, sizeof(*cfg));
14338c2ecf20Sopenharmony_ci	else
14348c2ecf20Sopenharmony_ci		dsb(ishst);
14358c2ecf20Sopenharmony_ci}
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_cistatic void wait_for_syncr(void __iomem *rdbase)
14388c2ecf20Sopenharmony_ci{
14398c2ecf20Sopenharmony_ci	while (readl_relaxed(rdbase + GICR_SYNCR) & 1)
14408c2ecf20Sopenharmony_ci		cpu_relax();
14418c2ecf20Sopenharmony_ci}
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_cistatic void __direct_lpi_inv(struct irq_data *d, u64 val)
14448c2ecf20Sopenharmony_ci{
14458c2ecf20Sopenharmony_ci	void __iomem *rdbase;
14468c2ecf20Sopenharmony_ci	unsigned long flags;
14478c2ecf20Sopenharmony_ci	int cpu;
14488c2ecf20Sopenharmony_ci
14498c2ecf20Sopenharmony_ci	/* Target the redistributor this LPI is currently routed to */
14508c2ecf20Sopenharmony_ci	cpu = irq_to_cpuid_lock(d, &flags);
14518c2ecf20Sopenharmony_ci	raw_spin_lock(&gic_data_rdist_cpu(cpu)->rd_lock);
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_ci	rdbase = per_cpu_ptr(gic_rdists->rdist, cpu)->rd_base;
14548c2ecf20Sopenharmony_ci	gic_write_lpir(val, rdbase + GICR_INVLPIR);
14558c2ecf20Sopenharmony_ci	wait_for_syncr(rdbase);
14568c2ecf20Sopenharmony_ci
14578c2ecf20Sopenharmony_ci	raw_spin_unlock(&gic_data_rdist_cpu(cpu)->rd_lock);
14588c2ecf20Sopenharmony_ci	irq_to_cpuid_unlock(d, flags);
14598c2ecf20Sopenharmony_ci}
14608c2ecf20Sopenharmony_ci
14618c2ecf20Sopenharmony_cistatic void direct_lpi_inv(struct irq_data *d)
14628c2ecf20Sopenharmony_ci{
14638c2ecf20Sopenharmony_ci	struct its_vlpi_map *map = get_vlpi_map(d);
14648c2ecf20Sopenharmony_ci	u64 val;
14658c2ecf20Sopenharmony_ci
14668c2ecf20Sopenharmony_ci	if (map) {
14678c2ecf20Sopenharmony_ci		struct its_device *its_dev = irq_data_get_irq_chip_data(d);
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_ci		WARN_ON(!is_v4_1(its_dev->its));
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_ci		val  = GICR_INVLPIR_V;
14728c2ecf20Sopenharmony_ci		val |= FIELD_PREP(GICR_INVLPIR_VPEID, map->vpe->vpe_id);
14738c2ecf20Sopenharmony_ci		val |= FIELD_PREP(GICR_INVLPIR_INTID, map->vintid);
14748c2ecf20Sopenharmony_ci	} else {
14758c2ecf20Sopenharmony_ci		val = d->hwirq;
14768c2ecf20Sopenharmony_ci	}
14778c2ecf20Sopenharmony_ci
14788c2ecf20Sopenharmony_ci	__direct_lpi_inv(d, val);
14798c2ecf20Sopenharmony_ci}
14808c2ecf20Sopenharmony_ci
14818c2ecf20Sopenharmony_cistatic void lpi_update_config(struct irq_data *d, u8 clr, u8 set)
14828c2ecf20Sopenharmony_ci{
14838c2ecf20Sopenharmony_ci	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
14848c2ecf20Sopenharmony_ci
14858c2ecf20Sopenharmony_ci	lpi_write_config(d, clr, set);
14868c2ecf20Sopenharmony_ci	if (gic_rdists->has_direct_lpi &&
14878c2ecf20Sopenharmony_ci	    (is_v4_1(its_dev->its) || !irqd_is_forwarded_to_vcpu(d)))
14888c2ecf20Sopenharmony_ci		direct_lpi_inv(d);
14898c2ecf20Sopenharmony_ci	else if (!irqd_is_forwarded_to_vcpu(d))
14908c2ecf20Sopenharmony_ci		its_send_inv(its_dev, its_get_event_id(d));
14918c2ecf20Sopenharmony_ci	else
14928c2ecf20Sopenharmony_ci		its_send_vinv(its_dev, its_get_event_id(d));
14938c2ecf20Sopenharmony_ci}
14948c2ecf20Sopenharmony_ci
14958c2ecf20Sopenharmony_cistatic void its_vlpi_set_doorbell(struct irq_data *d, bool enable)
14968c2ecf20Sopenharmony_ci{
14978c2ecf20Sopenharmony_ci	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
14988c2ecf20Sopenharmony_ci	u32 event = its_get_event_id(d);
14998c2ecf20Sopenharmony_ci	struct its_vlpi_map *map;
15008c2ecf20Sopenharmony_ci
15018c2ecf20Sopenharmony_ci	/*
15028c2ecf20Sopenharmony_ci	 * GICv4.1 does away with the per-LPI nonsense, nothing to do
15038c2ecf20Sopenharmony_ci	 * here.
15048c2ecf20Sopenharmony_ci	 */
15058c2ecf20Sopenharmony_ci	if (is_v4_1(its_dev->its))
15068c2ecf20Sopenharmony_ci		return;
15078c2ecf20Sopenharmony_ci
15088c2ecf20Sopenharmony_ci	map = dev_event_to_vlpi_map(its_dev, event);
15098c2ecf20Sopenharmony_ci
15108c2ecf20Sopenharmony_ci	if (map->db_enabled == enable)
15118c2ecf20Sopenharmony_ci		return;
15128c2ecf20Sopenharmony_ci
15138c2ecf20Sopenharmony_ci	map->db_enabled = enable;
15148c2ecf20Sopenharmony_ci
15158c2ecf20Sopenharmony_ci	/*
15168c2ecf20Sopenharmony_ci	 * More fun with the architecture:
15178c2ecf20Sopenharmony_ci	 *
15188c2ecf20Sopenharmony_ci	 * Ideally, we'd issue a VMAPTI to set the doorbell to its LPI
15198c2ecf20Sopenharmony_ci	 * value or to 1023, depending on the enable bit. But that
15208c2ecf20Sopenharmony_ci	 * would be issuing a mapping for an /existing/ DevID+EventID
15218c2ecf20Sopenharmony_ci	 * pair, which is UNPREDICTABLE. Instead, let's issue a VMOVI
15228c2ecf20Sopenharmony_ci	 * to the /same/ vPE, using this opportunity to adjust the
15238c2ecf20Sopenharmony_ci	 * doorbell. Mouahahahaha. We loves it, Precious.
15248c2ecf20Sopenharmony_ci	 */
15258c2ecf20Sopenharmony_ci	its_send_vmovi(its_dev, event);
15268c2ecf20Sopenharmony_ci}
15278c2ecf20Sopenharmony_ci
15288c2ecf20Sopenharmony_cistatic void its_mask_irq(struct irq_data *d)
15298c2ecf20Sopenharmony_ci{
15308c2ecf20Sopenharmony_ci	if (irqd_is_forwarded_to_vcpu(d))
15318c2ecf20Sopenharmony_ci		its_vlpi_set_doorbell(d, false);
15328c2ecf20Sopenharmony_ci
15338c2ecf20Sopenharmony_ci	lpi_update_config(d, LPI_PROP_ENABLED, 0);
15348c2ecf20Sopenharmony_ci}
15358c2ecf20Sopenharmony_ci
15368c2ecf20Sopenharmony_cistatic void its_unmask_irq(struct irq_data *d)
15378c2ecf20Sopenharmony_ci{
15388c2ecf20Sopenharmony_ci	if (irqd_is_forwarded_to_vcpu(d))
15398c2ecf20Sopenharmony_ci		its_vlpi_set_doorbell(d, true);
15408c2ecf20Sopenharmony_ci
15418c2ecf20Sopenharmony_ci	lpi_update_config(d, 0, LPI_PROP_ENABLED);
15428c2ecf20Sopenharmony_ci}
15438c2ecf20Sopenharmony_ci
15448c2ecf20Sopenharmony_cistatic __maybe_unused u32 its_read_lpi_count(struct irq_data *d, int cpu)
15458c2ecf20Sopenharmony_ci{
15468c2ecf20Sopenharmony_ci	if (irqd_affinity_is_managed(d))
15478c2ecf20Sopenharmony_ci		return atomic_read(&per_cpu_ptr(&cpu_lpi_count, cpu)->managed);
15488c2ecf20Sopenharmony_ci
15498c2ecf20Sopenharmony_ci	return atomic_read(&per_cpu_ptr(&cpu_lpi_count, cpu)->unmanaged);
15508c2ecf20Sopenharmony_ci}
15518c2ecf20Sopenharmony_ci
15528c2ecf20Sopenharmony_cistatic void its_inc_lpi_count(struct irq_data *d, int cpu)
15538c2ecf20Sopenharmony_ci{
15548c2ecf20Sopenharmony_ci	if (irqd_affinity_is_managed(d))
15558c2ecf20Sopenharmony_ci		atomic_inc(&per_cpu_ptr(&cpu_lpi_count, cpu)->managed);
15568c2ecf20Sopenharmony_ci	else
15578c2ecf20Sopenharmony_ci		atomic_inc(&per_cpu_ptr(&cpu_lpi_count, cpu)->unmanaged);
15588c2ecf20Sopenharmony_ci}
15598c2ecf20Sopenharmony_ci
15608c2ecf20Sopenharmony_cistatic void its_dec_lpi_count(struct irq_data *d, int cpu)
15618c2ecf20Sopenharmony_ci{
15628c2ecf20Sopenharmony_ci	if (irqd_affinity_is_managed(d))
15638c2ecf20Sopenharmony_ci		atomic_dec(&per_cpu_ptr(&cpu_lpi_count, cpu)->managed);
15648c2ecf20Sopenharmony_ci	else
15658c2ecf20Sopenharmony_ci		atomic_dec(&per_cpu_ptr(&cpu_lpi_count, cpu)->unmanaged);
15668c2ecf20Sopenharmony_ci}
15678c2ecf20Sopenharmony_ci
15688c2ecf20Sopenharmony_cistatic unsigned int cpumask_pick_least_loaded(struct irq_data *d,
15698c2ecf20Sopenharmony_ci					      const struct cpumask *cpu_mask)
15708c2ecf20Sopenharmony_ci{
15718c2ecf20Sopenharmony_ci	unsigned int cpu = nr_cpu_ids, tmp;
15728c2ecf20Sopenharmony_ci	int count = S32_MAX;
15738c2ecf20Sopenharmony_ci
15748c2ecf20Sopenharmony_ci	for_each_cpu(tmp, cpu_mask) {
15758c2ecf20Sopenharmony_ci		int this_count = its_read_lpi_count(d, tmp);
15768c2ecf20Sopenharmony_ci		if (this_count < count) {
15778c2ecf20Sopenharmony_ci			cpu = tmp;
15788c2ecf20Sopenharmony_ci		        count = this_count;
15798c2ecf20Sopenharmony_ci		}
15808c2ecf20Sopenharmony_ci	}
15818c2ecf20Sopenharmony_ci
15828c2ecf20Sopenharmony_ci	return cpu;
15838c2ecf20Sopenharmony_ci}
15848c2ecf20Sopenharmony_ci
15858c2ecf20Sopenharmony_ci/*
15868c2ecf20Sopenharmony_ci * As suggested by Thomas Gleixner in:
15878c2ecf20Sopenharmony_ci * https://lore.kernel.org/r/87h80q2aoc.fsf@nanos.tec.linutronix.de
15888c2ecf20Sopenharmony_ci */
15898c2ecf20Sopenharmony_cistatic int its_select_cpu(struct irq_data *d,
15908c2ecf20Sopenharmony_ci			  const struct cpumask *aff_mask)
15918c2ecf20Sopenharmony_ci{
15928c2ecf20Sopenharmony_ci	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
15938c2ecf20Sopenharmony_ci	cpumask_var_t tmpmask;
15948c2ecf20Sopenharmony_ci	int cpu, node;
15958c2ecf20Sopenharmony_ci
15968c2ecf20Sopenharmony_ci	if (!alloc_cpumask_var(&tmpmask, GFP_ATOMIC))
15978c2ecf20Sopenharmony_ci		return -ENOMEM;
15988c2ecf20Sopenharmony_ci
15998c2ecf20Sopenharmony_ci	node = its_dev->its->numa_node;
16008c2ecf20Sopenharmony_ci
16018c2ecf20Sopenharmony_ci	if (!irqd_affinity_is_managed(d)) {
16028c2ecf20Sopenharmony_ci		/* First try the NUMA node */
16038c2ecf20Sopenharmony_ci		if (node != NUMA_NO_NODE) {
16048c2ecf20Sopenharmony_ci			/*
16058c2ecf20Sopenharmony_ci			 * Try the intersection of the affinity mask and the
16068c2ecf20Sopenharmony_ci			 * node mask (and the online mask, just to be safe).
16078c2ecf20Sopenharmony_ci			 */
16088c2ecf20Sopenharmony_ci			cpumask_and(tmpmask, cpumask_of_node(node), aff_mask);
16098c2ecf20Sopenharmony_ci			cpumask_and(tmpmask, tmpmask, cpu_online_mask);
16108c2ecf20Sopenharmony_ci
16118c2ecf20Sopenharmony_ci			/*
16128c2ecf20Sopenharmony_ci			 * Ideally, we would check if the mask is empty, and
16138c2ecf20Sopenharmony_ci			 * try again on the full node here.
16148c2ecf20Sopenharmony_ci			 *
16158c2ecf20Sopenharmony_ci			 * But it turns out that the way ACPI describes the
16168c2ecf20Sopenharmony_ci			 * affinity for ITSs only deals about memory, and
16178c2ecf20Sopenharmony_ci			 * not target CPUs, so it cannot describe a single
16188c2ecf20Sopenharmony_ci			 * ITS placed next to two NUMA nodes.
16198c2ecf20Sopenharmony_ci			 *
16208c2ecf20Sopenharmony_ci			 * Instead, just fallback on the online mask. This
16218c2ecf20Sopenharmony_ci			 * diverges from Thomas' suggestion above.
16228c2ecf20Sopenharmony_ci			 */
16238c2ecf20Sopenharmony_ci			cpu = cpumask_pick_least_loaded(d, tmpmask);
16248c2ecf20Sopenharmony_ci			if (cpu < nr_cpu_ids)
16258c2ecf20Sopenharmony_ci				goto out;
16268c2ecf20Sopenharmony_ci
16278c2ecf20Sopenharmony_ci			/* If we can't cross sockets, give up */
16288c2ecf20Sopenharmony_ci			if ((its_dev->its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_23144))
16298c2ecf20Sopenharmony_ci				goto out;
16308c2ecf20Sopenharmony_ci
16318c2ecf20Sopenharmony_ci			/* If the above failed, expand the search */
16328c2ecf20Sopenharmony_ci		}
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_ci		/* Try the intersection of the affinity and online masks */
16358c2ecf20Sopenharmony_ci		cpumask_and(tmpmask, aff_mask, cpu_online_mask);
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_ci		/* If that doesn't fly, the online mask is the last resort */
16388c2ecf20Sopenharmony_ci		if (cpumask_empty(tmpmask))
16398c2ecf20Sopenharmony_ci			cpumask_copy(tmpmask, cpu_online_mask);
16408c2ecf20Sopenharmony_ci
16418c2ecf20Sopenharmony_ci		cpu = cpumask_pick_least_loaded(d, tmpmask);
16428c2ecf20Sopenharmony_ci	} else {
16438c2ecf20Sopenharmony_ci		cpumask_copy(tmpmask, aff_mask);
16448c2ecf20Sopenharmony_ci
16458c2ecf20Sopenharmony_ci		/* If we cannot cross sockets, limit the search to that node */
16468c2ecf20Sopenharmony_ci		if ((its_dev->its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_23144) &&
16478c2ecf20Sopenharmony_ci		    node != NUMA_NO_NODE)
16488c2ecf20Sopenharmony_ci			cpumask_and(tmpmask, tmpmask, cpumask_of_node(node));
16498c2ecf20Sopenharmony_ci
16508c2ecf20Sopenharmony_ci		cpu = cpumask_pick_least_loaded(d, tmpmask);
16518c2ecf20Sopenharmony_ci	}
16528c2ecf20Sopenharmony_ciout:
16538c2ecf20Sopenharmony_ci	free_cpumask_var(tmpmask);
16548c2ecf20Sopenharmony_ci
16558c2ecf20Sopenharmony_ci	pr_debug("IRQ%d -> %*pbl CPU%d\n", d->irq, cpumask_pr_args(aff_mask), cpu);
16568c2ecf20Sopenharmony_ci	return cpu;
16578c2ecf20Sopenharmony_ci}
16588c2ecf20Sopenharmony_ci
16598c2ecf20Sopenharmony_cistatic int its_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
16608c2ecf20Sopenharmony_ci			    bool force)
16618c2ecf20Sopenharmony_ci{
16628c2ecf20Sopenharmony_ci	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
16638c2ecf20Sopenharmony_ci	struct its_collection *target_col;
16648c2ecf20Sopenharmony_ci	u32 id = its_get_event_id(d);
16658c2ecf20Sopenharmony_ci	int cpu, prev_cpu;
16668c2ecf20Sopenharmony_ci
16678c2ecf20Sopenharmony_ci	/* A forwarded interrupt should use irq_set_vcpu_affinity */
16688c2ecf20Sopenharmony_ci	if (irqd_is_forwarded_to_vcpu(d))
16698c2ecf20Sopenharmony_ci		return -EINVAL;
16708c2ecf20Sopenharmony_ci
16718c2ecf20Sopenharmony_ci	prev_cpu = its_dev->event_map.col_map[id];
16728c2ecf20Sopenharmony_ci	its_dec_lpi_count(d, prev_cpu);
16738c2ecf20Sopenharmony_ci
16748c2ecf20Sopenharmony_ci	if (!force)
16758c2ecf20Sopenharmony_ci		cpu = its_select_cpu(d, mask_val);
16768c2ecf20Sopenharmony_ci	else
16778c2ecf20Sopenharmony_ci		cpu = cpumask_pick_least_loaded(d, mask_val);
16788c2ecf20Sopenharmony_ci
16798c2ecf20Sopenharmony_ci	if (cpu < 0 || cpu >= nr_cpu_ids)
16808c2ecf20Sopenharmony_ci		goto err;
16818c2ecf20Sopenharmony_ci
16828c2ecf20Sopenharmony_ci	/* don't set the affinity when the target cpu is same as current one */
16838c2ecf20Sopenharmony_ci	if (cpu != prev_cpu) {
16848c2ecf20Sopenharmony_ci		target_col = &its_dev->its->collections[cpu];
16858c2ecf20Sopenharmony_ci		its_send_movi(its_dev, target_col, id);
16868c2ecf20Sopenharmony_ci		its_dev->event_map.col_map[id] = cpu;
16878c2ecf20Sopenharmony_ci		irq_data_update_effective_affinity(d, cpumask_of(cpu));
16888c2ecf20Sopenharmony_ci	}
16898c2ecf20Sopenharmony_ci
16908c2ecf20Sopenharmony_ci	its_inc_lpi_count(d, cpu);
16918c2ecf20Sopenharmony_ci
16928c2ecf20Sopenharmony_ci	return IRQ_SET_MASK_OK_DONE;
16938c2ecf20Sopenharmony_ci
16948c2ecf20Sopenharmony_cierr:
16958c2ecf20Sopenharmony_ci	its_inc_lpi_count(d, prev_cpu);
16968c2ecf20Sopenharmony_ci	return -EINVAL;
16978c2ecf20Sopenharmony_ci}
16988c2ecf20Sopenharmony_ci
16998c2ecf20Sopenharmony_cistatic u64 its_irq_get_msi_base(struct its_device *its_dev)
17008c2ecf20Sopenharmony_ci{
17018c2ecf20Sopenharmony_ci	struct its_node *its = its_dev->its;
17028c2ecf20Sopenharmony_ci
17038c2ecf20Sopenharmony_ci	return its->phys_base + GITS_TRANSLATER;
17048c2ecf20Sopenharmony_ci}
17058c2ecf20Sopenharmony_ci
17068c2ecf20Sopenharmony_cistatic void its_irq_compose_msi_msg(struct irq_data *d, struct msi_msg *msg)
17078c2ecf20Sopenharmony_ci{
17088c2ecf20Sopenharmony_ci	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
17098c2ecf20Sopenharmony_ci	struct its_node *its;
17108c2ecf20Sopenharmony_ci	u64 addr;
17118c2ecf20Sopenharmony_ci
17128c2ecf20Sopenharmony_ci	its = its_dev->its;
17138c2ecf20Sopenharmony_ci	addr = its->get_msi_base(its_dev);
17148c2ecf20Sopenharmony_ci
17158c2ecf20Sopenharmony_ci	msg->address_lo		= lower_32_bits(addr);
17168c2ecf20Sopenharmony_ci	msg->address_hi		= upper_32_bits(addr);
17178c2ecf20Sopenharmony_ci	msg->data		= its_get_event_id(d);
17188c2ecf20Sopenharmony_ci
17198c2ecf20Sopenharmony_ci	iommu_dma_compose_msi_msg(irq_data_get_msi_desc(d), msg);
17208c2ecf20Sopenharmony_ci}
17218c2ecf20Sopenharmony_ci
17228c2ecf20Sopenharmony_cistatic int its_irq_set_irqchip_state(struct irq_data *d,
17238c2ecf20Sopenharmony_ci				     enum irqchip_irq_state which,
17248c2ecf20Sopenharmony_ci				     bool state)
17258c2ecf20Sopenharmony_ci{
17268c2ecf20Sopenharmony_ci	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
17278c2ecf20Sopenharmony_ci	u32 event = its_get_event_id(d);
17288c2ecf20Sopenharmony_ci
17298c2ecf20Sopenharmony_ci	if (which != IRQCHIP_STATE_PENDING)
17308c2ecf20Sopenharmony_ci		return -EINVAL;
17318c2ecf20Sopenharmony_ci
17328c2ecf20Sopenharmony_ci	if (irqd_is_forwarded_to_vcpu(d)) {
17338c2ecf20Sopenharmony_ci		if (state)
17348c2ecf20Sopenharmony_ci			its_send_vint(its_dev, event);
17358c2ecf20Sopenharmony_ci		else
17368c2ecf20Sopenharmony_ci			its_send_vclear(its_dev, event);
17378c2ecf20Sopenharmony_ci	} else {
17388c2ecf20Sopenharmony_ci		if (state)
17398c2ecf20Sopenharmony_ci			its_send_int(its_dev, event);
17408c2ecf20Sopenharmony_ci		else
17418c2ecf20Sopenharmony_ci			its_send_clear(its_dev, event);
17428c2ecf20Sopenharmony_ci	}
17438c2ecf20Sopenharmony_ci
17448c2ecf20Sopenharmony_ci	return 0;
17458c2ecf20Sopenharmony_ci}
17468c2ecf20Sopenharmony_ci
17478c2ecf20Sopenharmony_cistatic int its_irq_retrigger(struct irq_data *d)
17488c2ecf20Sopenharmony_ci{
17498c2ecf20Sopenharmony_ci	return !its_irq_set_irqchip_state(d, IRQCHIP_STATE_PENDING, true);
17508c2ecf20Sopenharmony_ci}
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_ci/*
17538c2ecf20Sopenharmony_ci * Two favourable cases:
17548c2ecf20Sopenharmony_ci *
17558c2ecf20Sopenharmony_ci * (a) Either we have a GICv4.1, and all vPEs have to be mapped at all times
17568c2ecf20Sopenharmony_ci *     for vSGI delivery
17578c2ecf20Sopenharmony_ci *
17588c2ecf20Sopenharmony_ci * (b) Or the ITSs do not use a list map, meaning that VMOVP is cheap enough
17598c2ecf20Sopenharmony_ci *     and we're better off mapping all VPEs always
17608c2ecf20Sopenharmony_ci *
17618c2ecf20Sopenharmony_ci * If neither (a) nor (b) is true, then we map vPEs on demand.
17628c2ecf20Sopenharmony_ci *
17638c2ecf20Sopenharmony_ci */
17648c2ecf20Sopenharmony_cistatic bool gic_requires_eager_mapping(void)
17658c2ecf20Sopenharmony_ci{
17668c2ecf20Sopenharmony_ci	if (!its_list_map || gic_rdists->has_rvpeid)
17678c2ecf20Sopenharmony_ci		return true;
17688c2ecf20Sopenharmony_ci
17698c2ecf20Sopenharmony_ci	return false;
17708c2ecf20Sopenharmony_ci}
17718c2ecf20Sopenharmony_ci
17728c2ecf20Sopenharmony_cistatic void its_map_vm(struct its_node *its, struct its_vm *vm)
17738c2ecf20Sopenharmony_ci{
17748c2ecf20Sopenharmony_ci	unsigned long flags;
17758c2ecf20Sopenharmony_ci
17768c2ecf20Sopenharmony_ci	if (gic_requires_eager_mapping())
17778c2ecf20Sopenharmony_ci		return;
17788c2ecf20Sopenharmony_ci
17798c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&vmovp_lock, flags);
17808c2ecf20Sopenharmony_ci
17818c2ecf20Sopenharmony_ci	/*
17828c2ecf20Sopenharmony_ci	 * If the VM wasn't mapped yet, iterate over the vpes and get
17838c2ecf20Sopenharmony_ci	 * them mapped now.
17848c2ecf20Sopenharmony_ci	 */
17858c2ecf20Sopenharmony_ci	vm->vlpi_count[its->list_nr]++;
17868c2ecf20Sopenharmony_ci
17878c2ecf20Sopenharmony_ci	if (vm->vlpi_count[its->list_nr] == 1) {
17888c2ecf20Sopenharmony_ci		int i;
17898c2ecf20Sopenharmony_ci
17908c2ecf20Sopenharmony_ci		for (i = 0; i < vm->nr_vpes; i++) {
17918c2ecf20Sopenharmony_ci			struct its_vpe *vpe = vm->vpes[i];
17928c2ecf20Sopenharmony_ci			struct irq_data *d = irq_get_irq_data(vpe->irq);
17938c2ecf20Sopenharmony_ci
17948c2ecf20Sopenharmony_ci			/* Map the VPE to the first possible CPU */
17958c2ecf20Sopenharmony_ci			vpe->col_idx = cpumask_first(cpu_online_mask);
17968c2ecf20Sopenharmony_ci			its_send_vmapp(its, vpe, true);
17978c2ecf20Sopenharmony_ci			its_send_vinvall(its, vpe);
17988c2ecf20Sopenharmony_ci			irq_data_update_effective_affinity(d, cpumask_of(vpe->col_idx));
17998c2ecf20Sopenharmony_ci		}
18008c2ecf20Sopenharmony_ci	}
18018c2ecf20Sopenharmony_ci
18028c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&vmovp_lock, flags);
18038c2ecf20Sopenharmony_ci}
18048c2ecf20Sopenharmony_ci
18058c2ecf20Sopenharmony_cistatic void its_unmap_vm(struct its_node *its, struct its_vm *vm)
18068c2ecf20Sopenharmony_ci{
18078c2ecf20Sopenharmony_ci	unsigned long flags;
18088c2ecf20Sopenharmony_ci
18098c2ecf20Sopenharmony_ci	/* Not using the ITS list? Everything is always mapped. */
18108c2ecf20Sopenharmony_ci	if (gic_requires_eager_mapping())
18118c2ecf20Sopenharmony_ci		return;
18128c2ecf20Sopenharmony_ci
18138c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&vmovp_lock, flags);
18148c2ecf20Sopenharmony_ci
18158c2ecf20Sopenharmony_ci	if (!--vm->vlpi_count[its->list_nr]) {
18168c2ecf20Sopenharmony_ci		int i;
18178c2ecf20Sopenharmony_ci
18188c2ecf20Sopenharmony_ci		for (i = 0; i < vm->nr_vpes; i++)
18198c2ecf20Sopenharmony_ci			its_send_vmapp(its, vm->vpes[i], false);
18208c2ecf20Sopenharmony_ci	}
18218c2ecf20Sopenharmony_ci
18228c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&vmovp_lock, flags);
18238c2ecf20Sopenharmony_ci}
18248c2ecf20Sopenharmony_ci
18258c2ecf20Sopenharmony_cistatic int its_vlpi_map(struct irq_data *d, struct its_cmd_info *info)
18268c2ecf20Sopenharmony_ci{
18278c2ecf20Sopenharmony_ci	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
18288c2ecf20Sopenharmony_ci	u32 event = its_get_event_id(d);
18298c2ecf20Sopenharmony_ci	int ret = 0;
18308c2ecf20Sopenharmony_ci
18318c2ecf20Sopenharmony_ci	if (!info->map)
18328c2ecf20Sopenharmony_ci		return -EINVAL;
18338c2ecf20Sopenharmony_ci
18348c2ecf20Sopenharmony_ci	raw_spin_lock(&its_dev->event_map.vlpi_lock);
18358c2ecf20Sopenharmony_ci
18368c2ecf20Sopenharmony_ci	if (!its_dev->event_map.vm) {
18378c2ecf20Sopenharmony_ci		struct its_vlpi_map *maps;
18388c2ecf20Sopenharmony_ci
18398c2ecf20Sopenharmony_ci		maps = kcalloc(its_dev->event_map.nr_lpis, sizeof(*maps),
18408c2ecf20Sopenharmony_ci			       GFP_ATOMIC);
18418c2ecf20Sopenharmony_ci		if (!maps) {
18428c2ecf20Sopenharmony_ci			ret = -ENOMEM;
18438c2ecf20Sopenharmony_ci			goto out;
18448c2ecf20Sopenharmony_ci		}
18458c2ecf20Sopenharmony_ci
18468c2ecf20Sopenharmony_ci		its_dev->event_map.vm = info->map->vm;
18478c2ecf20Sopenharmony_ci		its_dev->event_map.vlpi_maps = maps;
18488c2ecf20Sopenharmony_ci	} else if (its_dev->event_map.vm != info->map->vm) {
18498c2ecf20Sopenharmony_ci		ret = -EINVAL;
18508c2ecf20Sopenharmony_ci		goto out;
18518c2ecf20Sopenharmony_ci	}
18528c2ecf20Sopenharmony_ci
18538c2ecf20Sopenharmony_ci	/* Get our private copy of the mapping information */
18548c2ecf20Sopenharmony_ci	its_dev->event_map.vlpi_maps[event] = *info->map;
18558c2ecf20Sopenharmony_ci
18568c2ecf20Sopenharmony_ci	if (irqd_is_forwarded_to_vcpu(d)) {
18578c2ecf20Sopenharmony_ci		/* Already mapped, move it around */
18588c2ecf20Sopenharmony_ci		its_send_vmovi(its_dev, event);
18598c2ecf20Sopenharmony_ci	} else {
18608c2ecf20Sopenharmony_ci		/* Ensure all the VPEs are mapped on this ITS */
18618c2ecf20Sopenharmony_ci		its_map_vm(its_dev->its, info->map->vm);
18628c2ecf20Sopenharmony_ci
18638c2ecf20Sopenharmony_ci		/*
18648c2ecf20Sopenharmony_ci		 * Flag the interrupt as forwarded so that we can
18658c2ecf20Sopenharmony_ci		 * start poking the virtual property table.
18668c2ecf20Sopenharmony_ci		 */
18678c2ecf20Sopenharmony_ci		irqd_set_forwarded_to_vcpu(d);
18688c2ecf20Sopenharmony_ci
18698c2ecf20Sopenharmony_ci		/* Write out the property to the prop table */
18708c2ecf20Sopenharmony_ci		lpi_write_config(d, 0xff, info->map->properties);
18718c2ecf20Sopenharmony_ci
18728c2ecf20Sopenharmony_ci		/* Drop the physical mapping */
18738c2ecf20Sopenharmony_ci		its_send_discard(its_dev, event);
18748c2ecf20Sopenharmony_ci
18758c2ecf20Sopenharmony_ci		/* and install the virtual one */
18768c2ecf20Sopenharmony_ci		its_send_vmapti(its_dev, event);
18778c2ecf20Sopenharmony_ci
18788c2ecf20Sopenharmony_ci		/* Increment the number of VLPIs */
18798c2ecf20Sopenharmony_ci		its_dev->event_map.nr_vlpis++;
18808c2ecf20Sopenharmony_ci	}
18818c2ecf20Sopenharmony_ci
18828c2ecf20Sopenharmony_ciout:
18838c2ecf20Sopenharmony_ci	raw_spin_unlock(&its_dev->event_map.vlpi_lock);
18848c2ecf20Sopenharmony_ci	return ret;
18858c2ecf20Sopenharmony_ci}
18868c2ecf20Sopenharmony_ci
18878c2ecf20Sopenharmony_cistatic int its_vlpi_get(struct irq_data *d, struct its_cmd_info *info)
18888c2ecf20Sopenharmony_ci{
18898c2ecf20Sopenharmony_ci	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
18908c2ecf20Sopenharmony_ci	struct its_vlpi_map *map;
18918c2ecf20Sopenharmony_ci	int ret = 0;
18928c2ecf20Sopenharmony_ci
18938c2ecf20Sopenharmony_ci	raw_spin_lock(&its_dev->event_map.vlpi_lock);
18948c2ecf20Sopenharmony_ci
18958c2ecf20Sopenharmony_ci	map = get_vlpi_map(d);
18968c2ecf20Sopenharmony_ci
18978c2ecf20Sopenharmony_ci	if (!its_dev->event_map.vm || !map) {
18988c2ecf20Sopenharmony_ci		ret = -EINVAL;
18998c2ecf20Sopenharmony_ci		goto out;
19008c2ecf20Sopenharmony_ci	}
19018c2ecf20Sopenharmony_ci
19028c2ecf20Sopenharmony_ci	/* Copy our mapping information to the incoming request */
19038c2ecf20Sopenharmony_ci	*info->map = *map;
19048c2ecf20Sopenharmony_ci
19058c2ecf20Sopenharmony_ciout:
19068c2ecf20Sopenharmony_ci	raw_spin_unlock(&its_dev->event_map.vlpi_lock);
19078c2ecf20Sopenharmony_ci	return ret;
19088c2ecf20Sopenharmony_ci}
19098c2ecf20Sopenharmony_ci
19108c2ecf20Sopenharmony_cistatic int its_vlpi_unmap(struct irq_data *d)
19118c2ecf20Sopenharmony_ci{
19128c2ecf20Sopenharmony_ci	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
19138c2ecf20Sopenharmony_ci	u32 event = its_get_event_id(d);
19148c2ecf20Sopenharmony_ci	int ret = 0;
19158c2ecf20Sopenharmony_ci
19168c2ecf20Sopenharmony_ci	raw_spin_lock(&its_dev->event_map.vlpi_lock);
19178c2ecf20Sopenharmony_ci
19188c2ecf20Sopenharmony_ci	if (!its_dev->event_map.vm || !irqd_is_forwarded_to_vcpu(d)) {
19198c2ecf20Sopenharmony_ci		ret = -EINVAL;
19208c2ecf20Sopenharmony_ci		goto out;
19218c2ecf20Sopenharmony_ci	}
19228c2ecf20Sopenharmony_ci
19238c2ecf20Sopenharmony_ci	/* Drop the virtual mapping */
19248c2ecf20Sopenharmony_ci	its_send_discard(its_dev, event);
19258c2ecf20Sopenharmony_ci
19268c2ecf20Sopenharmony_ci	/* and restore the physical one */
19278c2ecf20Sopenharmony_ci	irqd_clr_forwarded_to_vcpu(d);
19288c2ecf20Sopenharmony_ci	its_send_mapti(its_dev, d->hwirq, event);
19298c2ecf20Sopenharmony_ci	lpi_update_config(d, 0xff, (LPI_PROP_DEFAULT_PRIO |
19308c2ecf20Sopenharmony_ci				    LPI_PROP_ENABLED |
19318c2ecf20Sopenharmony_ci				    LPI_PROP_GROUP1));
19328c2ecf20Sopenharmony_ci
19338c2ecf20Sopenharmony_ci	/* Potentially unmap the VM from this ITS */
19348c2ecf20Sopenharmony_ci	its_unmap_vm(its_dev->its, its_dev->event_map.vm);
19358c2ecf20Sopenharmony_ci
19368c2ecf20Sopenharmony_ci	/*
19378c2ecf20Sopenharmony_ci	 * Drop the refcount and make the device available again if
19388c2ecf20Sopenharmony_ci	 * this was the last VLPI.
19398c2ecf20Sopenharmony_ci	 */
19408c2ecf20Sopenharmony_ci	if (!--its_dev->event_map.nr_vlpis) {
19418c2ecf20Sopenharmony_ci		its_dev->event_map.vm = NULL;
19428c2ecf20Sopenharmony_ci		kfree(its_dev->event_map.vlpi_maps);
19438c2ecf20Sopenharmony_ci	}
19448c2ecf20Sopenharmony_ci
19458c2ecf20Sopenharmony_ciout:
19468c2ecf20Sopenharmony_ci	raw_spin_unlock(&its_dev->event_map.vlpi_lock);
19478c2ecf20Sopenharmony_ci	return ret;
19488c2ecf20Sopenharmony_ci}
19498c2ecf20Sopenharmony_ci
19508c2ecf20Sopenharmony_cistatic int its_vlpi_prop_update(struct irq_data *d, struct its_cmd_info *info)
19518c2ecf20Sopenharmony_ci{
19528c2ecf20Sopenharmony_ci	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
19538c2ecf20Sopenharmony_ci
19548c2ecf20Sopenharmony_ci	if (!its_dev->event_map.vm || !irqd_is_forwarded_to_vcpu(d))
19558c2ecf20Sopenharmony_ci		return -EINVAL;
19568c2ecf20Sopenharmony_ci
19578c2ecf20Sopenharmony_ci	if (info->cmd_type == PROP_UPDATE_AND_INV_VLPI)
19588c2ecf20Sopenharmony_ci		lpi_update_config(d, 0xff, info->config);
19598c2ecf20Sopenharmony_ci	else
19608c2ecf20Sopenharmony_ci		lpi_write_config(d, 0xff, info->config);
19618c2ecf20Sopenharmony_ci	its_vlpi_set_doorbell(d, !!(info->config & LPI_PROP_ENABLED));
19628c2ecf20Sopenharmony_ci
19638c2ecf20Sopenharmony_ci	return 0;
19648c2ecf20Sopenharmony_ci}
19658c2ecf20Sopenharmony_ci
19668c2ecf20Sopenharmony_cistatic int its_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu_info)
19678c2ecf20Sopenharmony_ci{
19688c2ecf20Sopenharmony_ci	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
19698c2ecf20Sopenharmony_ci	struct its_cmd_info *info = vcpu_info;
19708c2ecf20Sopenharmony_ci
19718c2ecf20Sopenharmony_ci	/* Need a v4 ITS */
19728c2ecf20Sopenharmony_ci	if (!is_v4(its_dev->its))
19738c2ecf20Sopenharmony_ci		return -EINVAL;
19748c2ecf20Sopenharmony_ci
19758c2ecf20Sopenharmony_ci	/* Unmap request? */
19768c2ecf20Sopenharmony_ci	if (!info)
19778c2ecf20Sopenharmony_ci		return its_vlpi_unmap(d);
19788c2ecf20Sopenharmony_ci
19798c2ecf20Sopenharmony_ci	switch (info->cmd_type) {
19808c2ecf20Sopenharmony_ci	case MAP_VLPI:
19818c2ecf20Sopenharmony_ci		return its_vlpi_map(d, info);
19828c2ecf20Sopenharmony_ci
19838c2ecf20Sopenharmony_ci	case GET_VLPI:
19848c2ecf20Sopenharmony_ci		return its_vlpi_get(d, info);
19858c2ecf20Sopenharmony_ci
19868c2ecf20Sopenharmony_ci	case PROP_UPDATE_VLPI:
19878c2ecf20Sopenharmony_ci	case PROP_UPDATE_AND_INV_VLPI:
19888c2ecf20Sopenharmony_ci		return its_vlpi_prop_update(d, info);
19898c2ecf20Sopenharmony_ci
19908c2ecf20Sopenharmony_ci	default:
19918c2ecf20Sopenharmony_ci		return -EINVAL;
19928c2ecf20Sopenharmony_ci	}
19938c2ecf20Sopenharmony_ci}
19948c2ecf20Sopenharmony_ci
19958c2ecf20Sopenharmony_cistatic struct irq_chip its_irq_chip = {
19968c2ecf20Sopenharmony_ci	.name			= "ITS",
19978c2ecf20Sopenharmony_ci	.irq_mask		= its_mask_irq,
19988c2ecf20Sopenharmony_ci	.irq_unmask		= its_unmask_irq,
19998c2ecf20Sopenharmony_ci	.irq_eoi		= irq_chip_eoi_parent,
20008c2ecf20Sopenharmony_ci	.irq_set_affinity	= its_set_affinity,
20018c2ecf20Sopenharmony_ci	.irq_compose_msi_msg	= its_irq_compose_msi_msg,
20028c2ecf20Sopenharmony_ci	.irq_set_irqchip_state	= its_irq_set_irqchip_state,
20038c2ecf20Sopenharmony_ci	.irq_retrigger		= its_irq_retrigger,
20048c2ecf20Sopenharmony_ci	.irq_set_vcpu_affinity	= its_irq_set_vcpu_affinity,
20058c2ecf20Sopenharmony_ci};
20068c2ecf20Sopenharmony_ci
20078c2ecf20Sopenharmony_ci
20088c2ecf20Sopenharmony_ci/*
20098c2ecf20Sopenharmony_ci * How we allocate LPIs:
20108c2ecf20Sopenharmony_ci *
20118c2ecf20Sopenharmony_ci * lpi_range_list contains ranges of LPIs that are to available to
20128c2ecf20Sopenharmony_ci * allocate from. To allocate LPIs, just pick the first range that
20138c2ecf20Sopenharmony_ci * fits the required allocation, and reduce it by the required
20148c2ecf20Sopenharmony_ci * amount. Once empty, remove the range from the list.
20158c2ecf20Sopenharmony_ci *
20168c2ecf20Sopenharmony_ci * To free a range of LPIs, add a free range to the list, sort it and
20178c2ecf20Sopenharmony_ci * merge the result if the new range happens to be adjacent to an
20188c2ecf20Sopenharmony_ci * already free block.
20198c2ecf20Sopenharmony_ci *
20208c2ecf20Sopenharmony_ci * The consequence of the above is that allocation is cost is low, but
20218c2ecf20Sopenharmony_ci * freeing is expensive. We assumes that freeing rarely occurs.
20228c2ecf20Sopenharmony_ci */
20238c2ecf20Sopenharmony_ci#define ITS_MAX_LPI_NRBITS	16 /* 64K LPIs */
20248c2ecf20Sopenharmony_ci
20258c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(lpi_range_lock);
20268c2ecf20Sopenharmony_cistatic LIST_HEAD(lpi_range_list);
20278c2ecf20Sopenharmony_ci
20288c2ecf20Sopenharmony_cistruct lpi_range {
20298c2ecf20Sopenharmony_ci	struct list_head	entry;
20308c2ecf20Sopenharmony_ci	u32			base_id;
20318c2ecf20Sopenharmony_ci	u32			span;
20328c2ecf20Sopenharmony_ci};
20338c2ecf20Sopenharmony_ci
20348c2ecf20Sopenharmony_cistatic struct lpi_range *mk_lpi_range(u32 base, u32 span)
20358c2ecf20Sopenharmony_ci{
20368c2ecf20Sopenharmony_ci	struct lpi_range *range;
20378c2ecf20Sopenharmony_ci
20388c2ecf20Sopenharmony_ci	range = kmalloc(sizeof(*range), GFP_KERNEL);
20398c2ecf20Sopenharmony_ci	if (range) {
20408c2ecf20Sopenharmony_ci		range->base_id = base;
20418c2ecf20Sopenharmony_ci		range->span = span;
20428c2ecf20Sopenharmony_ci	}
20438c2ecf20Sopenharmony_ci
20448c2ecf20Sopenharmony_ci	return range;
20458c2ecf20Sopenharmony_ci}
20468c2ecf20Sopenharmony_ci
20478c2ecf20Sopenharmony_cistatic int alloc_lpi_range(u32 nr_lpis, u32 *base)
20488c2ecf20Sopenharmony_ci{
20498c2ecf20Sopenharmony_ci	struct lpi_range *range, *tmp;
20508c2ecf20Sopenharmony_ci	int err = -ENOSPC;
20518c2ecf20Sopenharmony_ci
20528c2ecf20Sopenharmony_ci	mutex_lock(&lpi_range_lock);
20538c2ecf20Sopenharmony_ci
20548c2ecf20Sopenharmony_ci	list_for_each_entry_safe(range, tmp, &lpi_range_list, entry) {
20558c2ecf20Sopenharmony_ci		if (range->span >= nr_lpis) {
20568c2ecf20Sopenharmony_ci			*base = range->base_id;
20578c2ecf20Sopenharmony_ci			range->base_id += nr_lpis;
20588c2ecf20Sopenharmony_ci			range->span -= nr_lpis;
20598c2ecf20Sopenharmony_ci
20608c2ecf20Sopenharmony_ci			if (range->span == 0) {
20618c2ecf20Sopenharmony_ci				list_del(&range->entry);
20628c2ecf20Sopenharmony_ci				kfree(range);
20638c2ecf20Sopenharmony_ci			}
20648c2ecf20Sopenharmony_ci
20658c2ecf20Sopenharmony_ci			err = 0;
20668c2ecf20Sopenharmony_ci			break;
20678c2ecf20Sopenharmony_ci		}
20688c2ecf20Sopenharmony_ci	}
20698c2ecf20Sopenharmony_ci
20708c2ecf20Sopenharmony_ci	mutex_unlock(&lpi_range_lock);
20718c2ecf20Sopenharmony_ci
20728c2ecf20Sopenharmony_ci	pr_debug("ITS: alloc %u:%u\n", *base, nr_lpis);
20738c2ecf20Sopenharmony_ci	return err;
20748c2ecf20Sopenharmony_ci}
20758c2ecf20Sopenharmony_ci
20768c2ecf20Sopenharmony_cistatic void merge_lpi_ranges(struct lpi_range *a, struct lpi_range *b)
20778c2ecf20Sopenharmony_ci{
20788c2ecf20Sopenharmony_ci	if (&a->entry == &lpi_range_list || &b->entry == &lpi_range_list)
20798c2ecf20Sopenharmony_ci		return;
20808c2ecf20Sopenharmony_ci	if (a->base_id + a->span != b->base_id)
20818c2ecf20Sopenharmony_ci		return;
20828c2ecf20Sopenharmony_ci	b->base_id = a->base_id;
20838c2ecf20Sopenharmony_ci	b->span += a->span;
20848c2ecf20Sopenharmony_ci	list_del(&a->entry);
20858c2ecf20Sopenharmony_ci	kfree(a);
20868c2ecf20Sopenharmony_ci}
20878c2ecf20Sopenharmony_ci
20888c2ecf20Sopenharmony_cistatic int free_lpi_range(u32 base, u32 nr_lpis)
20898c2ecf20Sopenharmony_ci{
20908c2ecf20Sopenharmony_ci	struct lpi_range *new, *old;
20918c2ecf20Sopenharmony_ci
20928c2ecf20Sopenharmony_ci	new = mk_lpi_range(base, nr_lpis);
20938c2ecf20Sopenharmony_ci	if (!new)
20948c2ecf20Sopenharmony_ci		return -ENOMEM;
20958c2ecf20Sopenharmony_ci
20968c2ecf20Sopenharmony_ci	mutex_lock(&lpi_range_lock);
20978c2ecf20Sopenharmony_ci
20988c2ecf20Sopenharmony_ci	list_for_each_entry_reverse(old, &lpi_range_list, entry) {
20998c2ecf20Sopenharmony_ci		if (old->base_id < base)
21008c2ecf20Sopenharmony_ci			break;
21018c2ecf20Sopenharmony_ci	}
21028c2ecf20Sopenharmony_ci	/*
21038c2ecf20Sopenharmony_ci	 * old is the last element with ->base_id smaller than base,
21048c2ecf20Sopenharmony_ci	 * so new goes right after it. If there are no elements with
21058c2ecf20Sopenharmony_ci	 * ->base_id smaller than base, &old->entry ends up pointing
21068c2ecf20Sopenharmony_ci	 * at the head of the list, and inserting new it the start of
21078c2ecf20Sopenharmony_ci	 * the list is the right thing to do in that case as well.
21088c2ecf20Sopenharmony_ci	 */
21098c2ecf20Sopenharmony_ci	list_add(&new->entry, &old->entry);
21108c2ecf20Sopenharmony_ci	/*
21118c2ecf20Sopenharmony_ci	 * Now check if we can merge with the preceding and/or
21128c2ecf20Sopenharmony_ci	 * following ranges.
21138c2ecf20Sopenharmony_ci	 */
21148c2ecf20Sopenharmony_ci	merge_lpi_ranges(old, new);
21158c2ecf20Sopenharmony_ci	merge_lpi_ranges(new, list_next_entry(new, entry));
21168c2ecf20Sopenharmony_ci
21178c2ecf20Sopenharmony_ci	mutex_unlock(&lpi_range_lock);
21188c2ecf20Sopenharmony_ci	return 0;
21198c2ecf20Sopenharmony_ci}
21208c2ecf20Sopenharmony_ci
21218c2ecf20Sopenharmony_cistatic int __init its_lpi_init(u32 id_bits)
21228c2ecf20Sopenharmony_ci{
21238c2ecf20Sopenharmony_ci	u32 lpis = (1UL << id_bits) - 8192;
21248c2ecf20Sopenharmony_ci	u32 numlpis;
21258c2ecf20Sopenharmony_ci	int err;
21268c2ecf20Sopenharmony_ci
21278c2ecf20Sopenharmony_ci	numlpis = 1UL << GICD_TYPER_NUM_LPIS(gic_rdists->gicd_typer);
21288c2ecf20Sopenharmony_ci
21298c2ecf20Sopenharmony_ci	if (numlpis > 2 && !WARN_ON(numlpis > lpis)) {
21308c2ecf20Sopenharmony_ci		lpis = numlpis;
21318c2ecf20Sopenharmony_ci		pr_info("ITS: Using hypervisor restricted LPI range [%u]\n",
21328c2ecf20Sopenharmony_ci			lpis);
21338c2ecf20Sopenharmony_ci	}
21348c2ecf20Sopenharmony_ci
21358c2ecf20Sopenharmony_ci	/*
21368c2ecf20Sopenharmony_ci	 * Initializing the allocator is just the same as freeing the
21378c2ecf20Sopenharmony_ci	 * full range of LPIs.
21388c2ecf20Sopenharmony_ci	 */
21398c2ecf20Sopenharmony_ci	err = free_lpi_range(8192, lpis);
21408c2ecf20Sopenharmony_ci	pr_debug("ITS: Allocator initialized for %u LPIs\n", lpis);
21418c2ecf20Sopenharmony_ci	return err;
21428c2ecf20Sopenharmony_ci}
21438c2ecf20Sopenharmony_ci
21448c2ecf20Sopenharmony_cistatic unsigned long *its_lpi_alloc(int nr_irqs, u32 *base, int *nr_ids)
21458c2ecf20Sopenharmony_ci{
21468c2ecf20Sopenharmony_ci	unsigned long *bitmap = NULL;
21478c2ecf20Sopenharmony_ci	int err = 0;
21488c2ecf20Sopenharmony_ci
21498c2ecf20Sopenharmony_ci	do {
21508c2ecf20Sopenharmony_ci		err = alloc_lpi_range(nr_irqs, base);
21518c2ecf20Sopenharmony_ci		if (!err)
21528c2ecf20Sopenharmony_ci			break;
21538c2ecf20Sopenharmony_ci
21548c2ecf20Sopenharmony_ci		nr_irqs /= 2;
21558c2ecf20Sopenharmony_ci	} while (nr_irqs > 0);
21568c2ecf20Sopenharmony_ci
21578c2ecf20Sopenharmony_ci	if (!nr_irqs)
21588c2ecf20Sopenharmony_ci		err = -ENOSPC;
21598c2ecf20Sopenharmony_ci
21608c2ecf20Sopenharmony_ci	if (err)
21618c2ecf20Sopenharmony_ci		goto out;
21628c2ecf20Sopenharmony_ci
21638c2ecf20Sopenharmony_ci	bitmap = kcalloc(BITS_TO_LONGS(nr_irqs), sizeof (long), GFP_ATOMIC);
21648c2ecf20Sopenharmony_ci	if (!bitmap)
21658c2ecf20Sopenharmony_ci		goto out;
21668c2ecf20Sopenharmony_ci
21678c2ecf20Sopenharmony_ci	*nr_ids = nr_irqs;
21688c2ecf20Sopenharmony_ci
21698c2ecf20Sopenharmony_ciout:
21708c2ecf20Sopenharmony_ci	if (!bitmap)
21718c2ecf20Sopenharmony_ci		*base = *nr_ids = 0;
21728c2ecf20Sopenharmony_ci
21738c2ecf20Sopenharmony_ci	return bitmap;
21748c2ecf20Sopenharmony_ci}
21758c2ecf20Sopenharmony_ci
21768c2ecf20Sopenharmony_cistatic void its_lpi_free(unsigned long *bitmap, u32 base, u32 nr_ids)
21778c2ecf20Sopenharmony_ci{
21788c2ecf20Sopenharmony_ci	WARN_ON(free_lpi_range(base, nr_ids));
21798c2ecf20Sopenharmony_ci	kfree(bitmap);
21808c2ecf20Sopenharmony_ci}
21818c2ecf20Sopenharmony_ci
21828c2ecf20Sopenharmony_cistatic void gic_reset_prop_table(void *va)
21838c2ecf20Sopenharmony_ci{
21848c2ecf20Sopenharmony_ci	/* Priority 0xa0, Group-1, disabled */
21858c2ecf20Sopenharmony_ci	memset(va, LPI_PROP_DEFAULT_PRIO | LPI_PROP_GROUP1, LPI_PROPBASE_SZ);
21868c2ecf20Sopenharmony_ci
21878c2ecf20Sopenharmony_ci	/* Make sure the GIC will observe the written configuration */
21888c2ecf20Sopenharmony_ci	gic_flush_dcache_to_poc(va, LPI_PROPBASE_SZ);
21898c2ecf20Sopenharmony_ci}
21908c2ecf20Sopenharmony_ci
21918c2ecf20Sopenharmony_cistatic struct page *its_allocate_prop_table(gfp_t gfp_flags)
21928c2ecf20Sopenharmony_ci{
21938c2ecf20Sopenharmony_ci	struct page *prop_page;
21948c2ecf20Sopenharmony_ci
21958c2ecf20Sopenharmony_ci	prop_page = alloc_pages(gfp_flags, get_order(LPI_PROPBASE_SZ));
21968c2ecf20Sopenharmony_ci	if (!prop_page)
21978c2ecf20Sopenharmony_ci		return NULL;
21988c2ecf20Sopenharmony_ci
21998c2ecf20Sopenharmony_ci	gic_reset_prop_table(page_address(prop_page));
22008c2ecf20Sopenharmony_ci
22018c2ecf20Sopenharmony_ci	return prop_page;
22028c2ecf20Sopenharmony_ci}
22038c2ecf20Sopenharmony_ci
22048c2ecf20Sopenharmony_cistatic void its_free_prop_table(struct page *prop_page)
22058c2ecf20Sopenharmony_ci{
22068c2ecf20Sopenharmony_ci	free_pages((unsigned long)page_address(prop_page),
22078c2ecf20Sopenharmony_ci		   get_order(LPI_PROPBASE_SZ));
22088c2ecf20Sopenharmony_ci}
22098c2ecf20Sopenharmony_ci
22108c2ecf20Sopenharmony_cistatic bool gic_check_reserved_range(phys_addr_t addr, unsigned long size)
22118c2ecf20Sopenharmony_ci{
22128c2ecf20Sopenharmony_ci	phys_addr_t start, end, addr_end;
22138c2ecf20Sopenharmony_ci	u64 i;
22148c2ecf20Sopenharmony_ci
22158c2ecf20Sopenharmony_ci	/*
22168c2ecf20Sopenharmony_ci	 * We don't bother checking for a kdump kernel as by
22178c2ecf20Sopenharmony_ci	 * construction, the LPI tables are out of this kernel's
22188c2ecf20Sopenharmony_ci	 * memory map.
22198c2ecf20Sopenharmony_ci	 */
22208c2ecf20Sopenharmony_ci	if (is_kdump_kernel())
22218c2ecf20Sopenharmony_ci		return true;
22228c2ecf20Sopenharmony_ci
22238c2ecf20Sopenharmony_ci	addr_end = addr + size - 1;
22248c2ecf20Sopenharmony_ci
22258c2ecf20Sopenharmony_ci	for_each_reserved_mem_range(i, &start, &end) {
22268c2ecf20Sopenharmony_ci		if (addr >= start && addr_end <= end)
22278c2ecf20Sopenharmony_ci			return true;
22288c2ecf20Sopenharmony_ci	}
22298c2ecf20Sopenharmony_ci
22308c2ecf20Sopenharmony_ci	/* Not found, not a good sign... */
22318c2ecf20Sopenharmony_ci	pr_warn("GICv3: Expected reserved range [%pa:%pa], not found\n",
22328c2ecf20Sopenharmony_ci		&addr, &addr_end);
22338c2ecf20Sopenharmony_ci	add_taint(TAINT_CRAP, LOCKDEP_STILL_OK);
22348c2ecf20Sopenharmony_ci	return false;
22358c2ecf20Sopenharmony_ci}
22368c2ecf20Sopenharmony_ci
22378c2ecf20Sopenharmony_cistatic int gic_reserve_range(phys_addr_t addr, unsigned long size)
22388c2ecf20Sopenharmony_ci{
22398c2ecf20Sopenharmony_ci	if (efi_enabled(EFI_CONFIG_TABLES))
22408c2ecf20Sopenharmony_ci		return efi_mem_reserve_persistent(addr, size);
22418c2ecf20Sopenharmony_ci
22428c2ecf20Sopenharmony_ci	return 0;
22438c2ecf20Sopenharmony_ci}
22448c2ecf20Sopenharmony_ci
22458c2ecf20Sopenharmony_cistatic int __init its_setup_lpi_prop_table(void)
22468c2ecf20Sopenharmony_ci{
22478c2ecf20Sopenharmony_ci	if (gic_rdists->flags & RDIST_FLAGS_RD_TABLES_PREALLOCATED) {
22488c2ecf20Sopenharmony_ci		u64 val;
22498c2ecf20Sopenharmony_ci
22508c2ecf20Sopenharmony_ci		val = gicr_read_propbaser(gic_data_rdist_rd_base() + GICR_PROPBASER);
22518c2ecf20Sopenharmony_ci		lpi_id_bits = (val & GICR_PROPBASER_IDBITS_MASK) + 1;
22528c2ecf20Sopenharmony_ci
22538c2ecf20Sopenharmony_ci		gic_rdists->prop_table_pa = val & GENMASK_ULL(51, 12);
22548c2ecf20Sopenharmony_ci		gic_rdists->prop_table_va = memremap(gic_rdists->prop_table_pa,
22558c2ecf20Sopenharmony_ci						     LPI_PROPBASE_SZ,
22568c2ecf20Sopenharmony_ci						     MEMREMAP_WB);
22578c2ecf20Sopenharmony_ci		gic_reset_prop_table(gic_rdists->prop_table_va);
22588c2ecf20Sopenharmony_ci	} else {
22598c2ecf20Sopenharmony_ci		struct page *page;
22608c2ecf20Sopenharmony_ci
22618c2ecf20Sopenharmony_ci		lpi_id_bits = min_t(u32,
22628c2ecf20Sopenharmony_ci				    GICD_TYPER_ID_BITS(gic_rdists->gicd_typer),
22638c2ecf20Sopenharmony_ci				    ITS_MAX_LPI_NRBITS);
22648c2ecf20Sopenharmony_ci		page = its_allocate_prop_table(GFP_NOWAIT);
22658c2ecf20Sopenharmony_ci		if (!page) {
22668c2ecf20Sopenharmony_ci			pr_err("Failed to allocate PROPBASE\n");
22678c2ecf20Sopenharmony_ci			return -ENOMEM;
22688c2ecf20Sopenharmony_ci		}
22698c2ecf20Sopenharmony_ci
22708c2ecf20Sopenharmony_ci		gic_rdists->prop_table_pa = page_to_phys(page);
22718c2ecf20Sopenharmony_ci		gic_rdists->prop_table_va = page_address(page);
22728c2ecf20Sopenharmony_ci		WARN_ON(gic_reserve_range(gic_rdists->prop_table_pa,
22738c2ecf20Sopenharmony_ci					  LPI_PROPBASE_SZ));
22748c2ecf20Sopenharmony_ci	}
22758c2ecf20Sopenharmony_ci
22768c2ecf20Sopenharmony_ci	pr_info("GICv3: using LPI property table @%pa\n",
22778c2ecf20Sopenharmony_ci		&gic_rdists->prop_table_pa);
22788c2ecf20Sopenharmony_ci
22798c2ecf20Sopenharmony_ci	return its_lpi_init(lpi_id_bits);
22808c2ecf20Sopenharmony_ci}
22818c2ecf20Sopenharmony_ci
22828c2ecf20Sopenharmony_cistatic const char *its_base_type_string[] = {
22838c2ecf20Sopenharmony_ci	[GITS_BASER_TYPE_DEVICE]	= "Devices",
22848c2ecf20Sopenharmony_ci	[GITS_BASER_TYPE_VCPU]		= "Virtual CPUs",
22858c2ecf20Sopenharmony_ci	[GITS_BASER_TYPE_RESERVED3]	= "Reserved (3)",
22868c2ecf20Sopenharmony_ci	[GITS_BASER_TYPE_COLLECTION]	= "Interrupt Collections",
22878c2ecf20Sopenharmony_ci	[GITS_BASER_TYPE_RESERVED5] 	= "Reserved (5)",
22888c2ecf20Sopenharmony_ci	[GITS_BASER_TYPE_RESERVED6] 	= "Reserved (6)",
22898c2ecf20Sopenharmony_ci	[GITS_BASER_TYPE_RESERVED7] 	= "Reserved (7)",
22908c2ecf20Sopenharmony_ci};
22918c2ecf20Sopenharmony_ci
22928c2ecf20Sopenharmony_cistatic u64 its_read_baser(struct its_node *its, struct its_baser *baser)
22938c2ecf20Sopenharmony_ci{
22948c2ecf20Sopenharmony_ci	u32 idx = baser - its->tables;
22958c2ecf20Sopenharmony_ci
22968c2ecf20Sopenharmony_ci	return gits_read_baser(its->base + GITS_BASER + (idx << 3));
22978c2ecf20Sopenharmony_ci}
22988c2ecf20Sopenharmony_ci
22998c2ecf20Sopenharmony_cistatic void its_write_baser(struct its_node *its, struct its_baser *baser,
23008c2ecf20Sopenharmony_ci			    u64 val)
23018c2ecf20Sopenharmony_ci{
23028c2ecf20Sopenharmony_ci	u32 idx = baser - its->tables;
23038c2ecf20Sopenharmony_ci
23048c2ecf20Sopenharmony_ci	gits_write_baser(val, its->base + GITS_BASER + (idx << 3));
23058c2ecf20Sopenharmony_ci	baser->val = its_read_baser(its, baser);
23068c2ecf20Sopenharmony_ci}
23078c2ecf20Sopenharmony_ci
23088c2ecf20Sopenharmony_cistatic int its_setup_baser(struct its_node *its, struct its_baser *baser,
23098c2ecf20Sopenharmony_ci			   u64 cache, u64 shr, u32 order, bool indirect)
23108c2ecf20Sopenharmony_ci{
23118c2ecf20Sopenharmony_ci	u64 val = its_read_baser(its, baser);
23128c2ecf20Sopenharmony_ci	u64 esz = GITS_BASER_ENTRY_SIZE(val);
23138c2ecf20Sopenharmony_ci	u64 type = GITS_BASER_TYPE(val);
23148c2ecf20Sopenharmony_ci	u64 baser_phys, tmp;
23158c2ecf20Sopenharmony_ci	u32 alloc_pages, psz;
23168c2ecf20Sopenharmony_ci	struct page *page;
23178c2ecf20Sopenharmony_ci	void *base;
23188c2ecf20Sopenharmony_ci
23198c2ecf20Sopenharmony_ci	psz = baser->psz;
23208c2ecf20Sopenharmony_ci	alloc_pages = (PAGE_ORDER_TO_SIZE(order) / psz);
23218c2ecf20Sopenharmony_ci	if (alloc_pages > GITS_BASER_PAGES_MAX) {
23228c2ecf20Sopenharmony_ci		pr_warn("ITS@%pa: %s too large, reduce ITS pages %u->%u\n",
23238c2ecf20Sopenharmony_ci			&its->phys_base, its_base_type_string[type],
23248c2ecf20Sopenharmony_ci			alloc_pages, GITS_BASER_PAGES_MAX);
23258c2ecf20Sopenharmony_ci		alloc_pages = GITS_BASER_PAGES_MAX;
23268c2ecf20Sopenharmony_ci		order = get_order(GITS_BASER_PAGES_MAX * psz);
23278c2ecf20Sopenharmony_ci	}
23288c2ecf20Sopenharmony_ci
23298c2ecf20Sopenharmony_ci	page = alloc_pages_node(its->numa_node, GFP_KERNEL | __GFP_ZERO, order);
23308c2ecf20Sopenharmony_ci	if (!page)
23318c2ecf20Sopenharmony_ci		return -ENOMEM;
23328c2ecf20Sopenharmony_ci
23338c2ecf20Sopenharmony_ci	base = (void *)page_address(page);
23348c2ecf20Sopenharmony_ci	baser_phys = virt_to_phys(base);
23358c2ecf20Sopenharmony_ci
23368c2ecf20Sopenharmony_ci	/* Check if the physical address of the memory is above 48bits */
23378c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_ARM64_64K_PAGES) && (baser_phys >> 48)) {
23388c2ecf20Sopenharmony_ci
23398c2ecf20Sopenharmony_ci		/* 52bit PA is supported only when PageSize=64K */
23408c2ecf20Sopenharmony_ci		if (psz != SZ_64K) {
23418c2ecf20Sopenharmony_ci			pr_err("ITS: no 52bit PA support when psz=%d\n", psz);
23428c2ecf20Sopenharmony_ci			free_pages((unsigned long)base, order);
23438c2ecf20Sopenharmony_ci			return -ENXIO;
23448c2ecf20Sopenharmony_ci		}
23458c2ecf20Sopenharmony_ci
23468c2ecf20Sopenharmony_ci		/* Convert 52bit PA to 48bit field */
23478c2ecf20Sopenharmony_ci		baser_phys = GITS_BASER_PHYS_52_to_48(baser_phys);
23488c2ecf20Sopenharmony_ci	}
23498c2ecf20Sopenharmony_ci
23508c2ecf20Sopenharmony_ciretry_baser:
23518c2ecf20Sopenharmony_ci	val = (baser_phys					 |
23528c2ecf20Sopenharmony_ci		(type << GITS_BASER_TYPE_SHIFT)			 |
23538c2ecf20Sopenharmony_ci		((esz - 1) << GITS_BASER_ENTRY_SIZE_SHIFT)	 |
23548c2ecf20Sopenharmony_ci		((alloc_pages - 1) << GITS_BASER_PAGES_SHIFT)	 |
23558c2ecf20Sopenharmony_ci		cache						 |
23568c2ecf20Sopenharmony_ci		shr						 |
23578c2ecf20Sopenharmony_ci		GITS_BASER_VALID);
23588c2ecf20Sopenharmony_ci
23598c2ecf20Sopenharmony_ci	val |=	indirect ? GITS_BASER_INDIRECT : 0x0;
23608c2ecf20Sopenharmony_ci
23618c2ecf20Sopenharmony_ci	switch (psz) {
23628c2ecf20Sopenharmony_ci	case SZ_4K:
23638c2ecf20Sopenharmony_ci		val |= GITS_BASER_PAGE_SIZE_4K;
23648c2ecf20Sopenharmony_ci		break;
23658c2ecf20Sopenharmony_ci	case SZ_16K:
23668c2ecf20Sopenharmony_ci		val |= GITS_BASER_PAGE_SIZE_16K;
23678c2ecf20Sopenharmony_ci		break;
23688c2ecf20Sopenharmony_ci	case SZ_64K:
23698c2ecf20Sopenharmony_ci		val |= GITS_BASER_PAGE_SIZE_64K;
23708c2ecf20Sopenharmony_ci		break;
23718c2ecf20Sopenharmony_ci	}
23728c2ecf20Sopenharmony_ci
23738c2ecf20Sopenharmony_ci	its_write_baser(its, baser, val);
23748c2ecf20Sopenharmony_ci	tmp = baser->val;
23758c2ecf20Sopenharmony_ci
23768c2ecf20Sopenharmony_ci	if ((val ^ tmp) & GITS_BASER_SHAREABILITY_MASK) {
23778c2ecf20Sopenharmony_ci		/*
23788c2ecf20Sopenharmony_ci		 * Shareability didn't stick. Just use
23798c2ecf20Sopenharmony_ci		 * whatever the read reported, which is likely
23808c2ecf20Sopenharmony_ci		 * to be the only thing this redistributor
23818c2ecf20Sopenharmony_ci		 * supports. If that's zero, make it
23828c2ecf20Sopenharmony_ci		 * non-cacheable as well.
23838c2ecf20Sopenharmony_ci		 */
23848c2ecf20Sopenharmony_ci		shr = tmp & GITS_BASER_SHAREABILITY_MASK;
23858c2ecf20Sopenharmony_ci		if (!shr) {
23868c2ecf20Sopenharmony_ci			cache = GITS_BASER_nC;
23878c2ecf20Sopenharmony_ci			gic_flush_dcache_to_poc(base, PAGE_ORDER_TO_SIZE(order));
23888c2ecf20Sopenharmony_ci		}
23898c2ecf20Sopenharmony_ci		goto retry_baser;
23908c2ecf20Sopenharmony_ci	}
23918c2ecf20Sopenharmony_ci
23928c2ecf20Sopenharmony_ci	if (val != tmp) {
23938c2ecf20Sopenharmony_ci		pr_err("ITS@%pa: %s doesn't stick: %llx %llx\n",
23948c2ecf20Sopenharmony_ci		       &its->phys_base, its_base_type_string[type],
23958c2ecf20Sopenharmony_ci		       val, tmp);
23968c2ecf20Sopenharmony_ci		free_pages((unsigned long)base, order);
23978c2ecf20Sopenharmony_ci		return -ENXIO;
23988c2ecf20Sopenharmony_ci	}
23998c2ecf20Sopenharmony_ci
24008c2ecf20Sopenharmony_ci	baser->order = order;
24018c2ecf20Sopenharmony_ci	baser->base = base;
24028c2ecf20Sopenharmony_ci	baser->psz = psz;
24038c2ecf20Sopenharmony_ci	tmp = indirect ? GITS_LVL1_ENTRY_SIZE : esz;
24048c2ecf20Sopenharmony_ci
24058c2ecf20Sopenharmony_ci	pr_info("ITS@%pa: allocated %d %s @%lx (%s, esz %d, psz %dK, shr %d)\n",
24068c2ecf20Sopenharmony_ci		&its->phys_base, (int)(PAGE_ORDER_TO_SIZE(order) / (int)tmp),
24078c2ecf20Sopenharmony_ci		its_base_type_string[type],
24088c2ecf20Sopenharmony_ci		(unsigned long)virt_to_phys(base),
24098c2ecf20Sopenharmony_ci		indirect ? "indirect" : "flat", (int)esz,
24108c2ecf20Sopenharmony_ci		psz / SZ_1K, (int)shr >> GITS_BASER_SHAREABILITY_SHIFT);
24118c2ecf20Sopenharmony_ci
24128c2ecf20Sopenharmony_ci	return 0;
24138c2ecf20Sopenharmony_ci}
24148c2ecf20Sopenharmony_ci
24158c2ecf20Sopenharmony_cistatic bool its_parse_indirect_baser(struct its_node *its,
24168c2ecf20Sopenharmony_ci				     struct its_baser *baser,
24178c2ecf20Sopenharmony_ci				     u32 *order, u32 ids)
24188c2ecf20Sopenharmony_ci{
24198c2ecf20Sopenharmony_ci	u64 tmp = its_read_baser(its, baser);
24208c2ecf20Sopenharmony_ci	u64 type = GITS_BASER_TYPE(tmp);
24218c2ecf20Sopenharmony_ci	u64 esz = GITS_BASER_ENTRY_SIZE(tmp);
24228c2ecf20Sopenharmony_ci	u64 val = GITS_BASER_InnerShareable | GITS_BASER_RaWaWb;
24238c2ecf20Sopenharmony_ci	u32 new_order = *order;
24248c2ecf20Sopenharmony_ci	u32 psz = baser->psz;
24258c2ecf20Sopenharmony_ci	bool indirect = false;
24268c2ecf20Sopenharmony_ci
24278c2ecf20Sopenharmony_ci	/* No need to enable Indirection if memory requirement < (psz*2)bytes */
24288c2ecf20Sopenharmony_ci	if ((esz << ids) > (psz * 2)) {
24298c2ecf20Sopenharmony_ci		/*
24308c2ecf20Sopenharmony_ci		 * Find out whether hw supports a single or two-level table by
24318c2ecf20Sopenharmony_ci		 * table by reading bit at offset '62' after writing '1' to it.
24328c2ecf20Sopenharmony_ci		 */
24338c2ecf20Sopenharmony_ci		its_write_baser(its, baser, val | GITS_BASER_INDIRECT);
24348c2ecf20Sopenharmony_ci		indirect = !!(baser->val & GITS_BASER_INDIRECT);
24358c2ecf20Sopenharmony_ci
24368c2ecf20Sopenharmony_ci		if (indirect) {
24378c2ecf20Sopenharmony_ci			/*
24388c2ecf20Sopenharmony_ci			 * The size of the lvl2 table is equal to ITS page size
24398c2ecf20Sopenharmony_ci			 * which is 'psz'. For computing lvl1 table size,
24408c2ecf20Sopenharmony_ci			 * subtract ID bits that sparse lvl2 table from 'ids'
24418c2ecf20Sopenharmony_ci			 * which is reported by ITS hardware times lvl1 table
24428c2ecf20Sopenharmony_ci			 * entry size.
24438c2ecf20Sopenharmony_ci			 */
24448c2ecf20Sopenharmony_ci			ids -= ilog2(psz / (int)esz);
24458c2ecf20Sopenharmony_ci			esz = GITS_LVL1_ENTRY_SIZE;
24468c2ecf20Sopenharmony_ci		}
24478c2ecf20Sopenharmony_ci	}
24488c2ecf20Sopenharmony_ci
24498c2ecf20Sopenharmony_ci	/*
24508c2ecf20Sopenharmony_ci	 * Allocate as many entries as required to fit the
24518c2ecf20Sopenharmony_ci	 * range of device IDs that the ITS can grok... The ID
24528c2ecf20Sopenharmony_ci	 * space being incredibly sparse, this results in a
24538c2ecf20Sopenharmony_ci	 * massive waste of memory if two-level device table
24548c2ecf20Sopenharmony_ci	 * feature is not supported by hardware.
24558c2ecf20Sopenharmony_ci	 */
24568c2ecf20Sopenharmony_ci	new_order = max_t(u32, get_order(esz << ids), new_order);
24578c2ecf20Sopenharmony_ci	if (new_order >= MAX_ORDER) {
24588c2ecf20Sopenharmony_ci		new_order = MAX_ORDER - 1;
24598c2ecf20Sopenharmony_ci		ids = ilog2(PAGE_ORDER_TO_SIZE(new_order) / (int)esz);
24608c2ecf20Sopenharmony_ci		pr_warn("ITS@%pa: %s Table too large, reduce ids %llu->%u\n",
24618c2ecf20Sopenharmony_ci			&its->phys_base, its_base_type_string[type],
24628c2ecf20Sopenharmony_ci			device_ids(its), ids);
24638c2ecf20Sopenharmony_ci	}
24648c2ecf20Sopenharmony_ci
24658c2ecf20Sopenharmony_ci	*order = new_order;
24668c2ecf20Sopenharmony_ci
24678c2ecf20Sopenharmony_ci	return indirect;
24688c2ecf20Sopenharmony_ci}
24698c2ecf20Sopenharmony_ci
24708c2ecf20Sopenharmony_cistatic u32 compute_common_aff(u64 val)
24718c2ecf20Sopenharmony_ci{
24728c2ecf20Sopenharmony_ci	u32 aff, clpiaff;
24738c2ecf20Sopenharmony_ci
24748c2ecf20Sopenharmony_ci	aff = FIELD_GET(GICR_TYPER_AFFINITY, val);
24758c2ecf20Sopenharmony_ci	clpiaff = FIELD_GET(GICR_TYPER_COMMON_LPI_AFF, val);
24768c2ecf20Sopenharmony_ci
24778c2ecf20Sopenharmony_ci	return aff & ~(GENMASK(31, 0) >> (clpiaff * 8));
24788c2ecf20Sopenharmony_ci}
24798c2ecf20Sopenharmony_ci
24808c2ecf20Sopenharmony_cistatic u32 compute_its_aff(struct its_node *its)
24818c2ecf20Sopenharmony_ci{
24828c2ecf20Sopenharmony_ci	u64 val;
24838c2ecf20Sopenharmony_ci	u32 svpet;
24848c2ecf20Sopenharmony_ci
24858c2ecf20Sopenharmony_ci	/*
24868c2ecf20Sopenharmony_ci	 * Reencode the ITS SVPET and MPIDR as a GICR_TYPER, and compute
24878c2ecf20Sopenharmony_ci	 * the resulting affinity. We then use that to see if this match
24888c2ecf20Sopenharmony_ci	 * our own affinity.
24898c2ecf20Sopenharmony_ci	 */
24908c2ecf20Sopenharmony_ci	svpet = FIELD_GET(GITS_TYPER_SVPET, its->typer);
24918c2ecf20Sopenharmony_ci	val  = FIELD_PREP(GICR_TYPER_COMMON_LPI_AFF, svpet);
24928c2ecf20Sopenharmony_ci	val |= FIELD_PREP(GICR_TYPER_AFFINITY, its->mpidr);
24938c2ecf20Sopenharmony_ci	return compute_common_aff(val);
24948c2ecf20Sopenharmony_ci}
24958c2ecf20Sopenharmony_ci
24968c2ecf20Sopenharmony_cistatic struct its_node *find_sibling_its(struct its_node *cur_its)
24978c2ecf20Sopenharmony_ci{
24988c2ecf20Sopenharmony_ci	struct its_node *its;
24998c2ecf20Sopenharmony_ci	u32 aff;
25008c2ecf20Sopenharmony_ci
25018c2ecf20Sopenharmony_ci	if (!FIELD_GET(GITS_TYPER_SVPET, cur_its->typer))
25028c2ecf20Sopenharmony_ci		return NULL;
25038c2ecf20Sopenharmony_ci
25048c2ecf20Sopenharmony_ci	aff = compute_its_aff(cur_its);
25058c2ecf20Sopenharmony_ci
25068c2ecf20Sopenharmony_ci	list_for_each_entry(its, &its_nodes, entry) {
25078c2ecf20Sopenharmony_ci		u64 baser;
25088c2ecf20Sopenharmony_ci
25098c2ecf20Sopenharmony_ci		if (!is_v4_1(its) || its == cur_its)
25108c2ecf20Sopenharmony_ci			continue;
25118c2ecf20Sopenharmony_ci
25128c2ecf20Sopenharmony_ci		if (!FIELD_GET(GITS_TYPER_SVPET, its->typer))
25138c2ecf20Sopenharmony_ci			continue;
25148c2ecf20Sopenharmony_ci
25158c2ecf20Sopenharmony_ci		if (aff != compute_its_aff(its))
25168c2ecf20Sopenharmony_ci			continue;
25178c2ecf20Sopenharmony_ci
25188c2ecf20Sopenharmony_ci		/* GICv4.1 guarantees that the vPE table is GITS_BASER2 */
25198c2ecf20Sopenharmony_ci		baser = its->tables[2].val;
25208c2ecf20Sopenharmony_ci		if (!(baser & GITS_BASER_VALID))
25218c2ecf20Sopenharmony_ci			continue;
25228c2ecf20Sopenharmony_ci
25238c2ecf20Sopenharmony_ci		return its;
25248c2ecf20Sopenharmony_ci	}
25258c2ecf20Sopenharmony_ci
25268c2ecf20Sopenharmony_ci	return NULL;
25278c2ecf20Sopenharmony_ci}
25288c2ecf20Sopenharmony_ci
25298c2ecf20Sopenharmony_cistatic void its_free_tables(struct its_node *its)
25308c2ecf20Sopenharmony_ci{
25318c2ecf20Sopenharmony_ci	int i;
25328c2ecf20Sopenharmony_ci
25338c2ecf20Sopenharmony_ci	for (i = 0; i < GITS_BASER_NR_REGS; i++) {
25348c2ecf20Sopenharmony_ci		if (its->tables[i].base) {
25358c2ecf20Sopenharmony_ci			free_pages((unsigned long)its->tables[i].base,
25368c2ecf20Sopenharmony_ci				   its->tables[i].order);
25378c2ecf20Sopenharmony_ci			its->tables[i].base = NULL;
25388c2ecf20Sopenharmony_ci		}
25398c2ecf20Sopenharmony_ci	}
25408c2ecf20Sopenharmony_ci}
25418c2ecf20Sopenharmony_ci
25428c2ecf20Sopenharmony_cistatic int its_probe_baser_psz(struct its_node *its, struct its_baser *baser)
25438c2ecf20Sopenharmony_ci{
25448c2ecf20Sopenharmony_ci	u64 psz = SZ_64K;
25458c2ecf20Sopenharmony_ci
25468c2ecf20Sopenharmony_ci	while (psz) {
25478c2ecf20Sopenharmony_ci		u64 val, gpsz;
25488c2ecf20Sopenharmony_ci
25498c2ecf20Sopenharmony_ci		val = its_read_baser(its, baser);
25508c2ecf20Sopenharmony_ci		val &= ~GITS_BASER_PAGE_SIZE_MASK;
25518c2ecf20Sopenharmony_ci
25528c2ecf20Sopenharmony_ci		switch (psz) {
25538c2ecf20Sopenharmony_ci		case SZ_64K:
25548c2ecf20Sopenharmony_ci			gpsz = GITS_BASER_PAGE_SIZE_64K;
25558c2ecf20Sopenharmony_ci			break;
25568c2ecf20Sopenharmony_ci		case SZ_16K:
25578c2ecf20Sopenharmony_ci			gpsz = GITS_BASER_PAGE_SIZE_16K;
25588c2ecf20Sopenharmony_ci			break;
25598c2ecf20Sopenharmony_ci		case SZ_4K:
25608c2ecf20Sopenharmony_ci		default:
25618c2ecf20Sopenharmony_ci			gpsz = GITS_BASER_PAGE_SIZE_4K;
25628c2ecf20Sopenharmony_ci			break;
25638c2ecf20Sopenharmony_ci		}
25648c2ecf20Sopenharmony_ci
25658c2ecf20Sopenharmony_ci		gpsz >>= GITS_BASER_PAGE_SIZE_SHIFT;
25668c2ecf20Sopenharmony_ci
25678c2ecf20Sopenharmony_ci		val |= FIELD_PREP(GITS_BASER_PAGE_SIZE_MASK, gpsz);
25688c2ecf20Sopenharmony_ci		its_write_baser(its, baser, val);
25698c2ecf20Sopenharmony_ci
25708c2ecf20Sopenharmony_ci		if (FIELD_GET(GITS_BASER_PAGE_SIZE_MASK, baser->val) == gpsz)
25718c2ecf20Sopenharmony_ci			break;
25728c2ecf20Sopenharmony_ci
25738c2ecf20Sopenharmony_ci		switch (psz) {
25748c2ecf20Sopenharmony_ci		case SZ_64K:
25758c2ecf20Sopenharmony_ci			psz = SZ_16K;
25768c2ecf20Sopenharmony_ci			break;
25778c2ecf20Sopenharmony_ci		case SZ_16K:
25788c2ecf20Sopenharmony_ci			psz = SZ_4K;
25798c2ecf20Sopenharmony_ci			break;
25808c2ecf20Sopenharmony_ci		case SZ_4K:
25818c2ecf20Sopenharmony_ci		default:
25828c2ecf20Sopenharmony_ci			return -1;
25838c2ecf20Sopenharmony_ci		}
25848c2ecf20Sopenharmony_ci	}
25858c2ecf20Sopenharmony_ci
25868c2ecf20Sopenharmony_ci	baser->psz = psz;
25878c2ecf20Sopenharmony_ci	return 0;
25888c2ecf20Sopenharmony_ci}
25898c2ecf20Sopenharmony_ci
25908c2ecf20Sopenharmony_cistatic int its_alloc_tables(struct its_node *its)
25918c2ecf20Sopenharmony_ci{
25928c2ecf20Sopenharmony_ci	u64 shr = GITS_BASER_InnerShareable;
25938c2ecf20Sopenharmony_ci	u64 cache = GITS_BASER_RaWaWb;
25948c2ecf20Sopenharmony_ci	int err, i;
25958c2ecf20Sopenharmony_ci
25968c2ecf20Sopenharmony_ci	if (its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_22375)
25978c2ecf20Sopenharmony_ci		/* erratum 24313: ignore memory access type */
25988c2ecf20Sopenharmony_ci		cache = GITS_BASER_nCnB;
25998c2ecf20Sopenharmony_ci
26008c2ecf20Sopenharmony_ci	for (i = 0; i < GITS_BASER_NR_REGS; i++) {
26018c2ecf20Sopenharmony_ci		struct its_baser *baser = its->tables + i;
26028c2ecf20Sopenharmony_ci		u64 val = its_read_baser(its, baser);
26038c2ecf20Sopenharmony_ci		u64 type = GITS_BASER_TYPE(val);
26048c2ecf20Sopenharmony_ci		bool indirect = false;
26058c2ecf20Sopenharmony_ci		u32 order;
26068c2ecf20Sopenharmony_ci
26078c2ecf20Sopenharmony_ci		if (type == GITS_BASER_TYPE_NONE)
26088c2ecf20Sopenharmony_ci			continue;
26098c2ecf20Sopenharmony_ci
26108c2ecf20Sopenharmony_ci		if (its_probe_baser_psz(its, baser)) {
26118c2ecf20Sopenharmony_ci			its_free_tables(its);
26128c2ecf20Sopenharmony_ci			return -ENXIO;
26138c2ecf20Sopenharmony_ci		}
26148c2ecf20Sopenharmony_ci
26158c2ecf20Sopenharmony_ci		order = get_order(baser->psz);
26168c2ecf20Sopenharmony_ci
26178c2ecf20Sopenharmony_ci		switch (type) {
26188c2ecf20Sopenharmony_ci		case GITS_BASER_TYPE_DEVICE:
26198c2ecf20Sopenharmony_ci			indirect = its_parse_indirect_baser(its, baser, &order,
26208c2ecf20Sopenharmony_ci							    device_ids(its));
26218c2ecf20Sopenharmony_ci			break;
26228c2ecf20Sopenharmony_ci
26238c2ecf20Sopenharmony_ci		case GITS_BASER_TYPE_VCPU:
26248c2ecf20Sopenharmony_ci			if (is_v4_1(its)) {
26258c2ecf20Sopenharmony_ci				struct its_node *sibling;
26268c2ecf20Sopenharmony_ci
26278c2ecf20Sopenharmony_ci				WARN_ON(i != 2);
26288c2ecf20Sopenharmony_ci				if ((sibling = find_sibling_its(its))) {
26298c2ecf20Sopenharmony_ci					*baser = sibling->tables[2];
26308c2ecf20Sopenharmony_ci					its_write_baser(its, baser, baser->val);
26318c2ecf20Sopenharmony_ci					continue;
26328c2ecf20Sopenharmony_ci				}
26338c2ecf20Sopenharmony_ci			}
26348c2ecf20Sopenharmony_ci
26358c2ecf20Sopenharmony_ci			indirect = its_parse_indirect_baser(its, baser, &order,
26368c2ecf20Sopenharmony_ci							    ITS_MAX_VPEID_BITS);
26378c2ecf20Sopenharmony_ci			break;
26388c2ecf20Sopenharmony_ci		}
26398c2ecf20Sopenharmony_ci
26408c2ecf20Sopenharmony_ci		err = its_setup_baser(its, baser, cache, shr, order, indirect);
26418c2ecf20Sopenharmony_ci		if (err < 0) {
26428c2ecf20Sopenharmony_ci			its_free_tables(its);
26438c2ecf20Sopenharmony_ci			return err;
26448c2ecf20Sopenharmony_ci		}
26458c2ecf20Sopenharmony_ci
26468c2ecf20Sopenharmony_ci		/* Update settings which will be used for next BASERn */
26478c2ecf20Sopenharmony_ci		cache = baser->val & GITS_BASER_CACHEABILITY_MASK;
26488c2ecf20Sopenharmony_ci		shr = baser->val & GITS_BASER_SHAREABILITY_MASK;
26498c2ecf20Sopenharmony_ci	}
26508c2ecf20Sopenharmony_ci
26518c2ecf20Sopenharmony_ci	return 0;
26528c2ecf20Sopenharmony_ci}
26538c2ecf20Sopenharmony_ci
26548c2ecf20Sopenharmony_cistatic u64 inherit_vpe_l1_table_from_its(void)
26558c2ecf20Sopenharmony_ci{
26568c2ecf20Sopenharmony_ci	struct its_node *its;
26578c2ecf20Sopenharmony_ci	u64 val;
26588c2ecf20Sopenharmony_ci	u32 aff;
26598c2ecf20Sopenharmony_ci
26608c2ecf20Sopenharmony_ci	val = gic_read_typer(gic_data_rdist_rd_base() + GICR_TYPER);
26618c2ecf20Sopenharmony_ci	aff = compute_common_aff(val);
26628c2ecf20Sopenharmony_ci
26638c2ecf20Sopenharmony_ci	list_for_each_entry(its, &its_nodes, entry) {
26648c2ecf20Sopenharmony_ci		u64 baser, addr;
26658c2ecf20Sopenharmony_ci
26668c2ecf20Sopenharmony_ci		if (!is_v4_1(its))
26678c2ecf20Sopenharmony_ci			continue;
26688c2ecf20Sopenharmony_ci
26698c2ecf20Sopenharmony_ci		if (!FIELD_GET(GITS_TYPER_SVPET, its->typer))
26708c2ecf20Sopenharmony_ci			continue;
26718c2ecf20Sopenharmony_ci
26728c2ecf20Sopenharmony_ci		if (aff != compute_its_aff(its))
26738c2ecf20Sopenharmony_ci			continue;
26748c2ecf20Sopenharmony_ci
26758c2ecf20Sopenharmony_ci		/* GICv4.1 guarantees that the vPE table is GITS_BASER2 */
26768c2ecf20Sopenharmony_ci		baser = its->tables[2].val;
26778c2ecf20Sopenharmony_ci		if (!(baser & GITS_BASER_VALID))
26788c2ecf20Sopenharmony_ci			continue;
26798c2ecf20Sopenharmony_ci
26808c2ecf20Sopenharmony_ci		/* We have a winner! */
26818c2ecf20Sopenharmony_ci		gic_data_rdist()->vpe_l1_base = its->tables[2].base;
26828c2ecf20Sopenharmony_ci
26838c2ecf20Sopenharmony_ci		val  = GICR_VPROPBASER_4_1_VALID;
26848c2ecf20Sopenharmony_ci		if (baser & GITS_BASER_INDIRECT)
26858c2ecf20Sopenharmony_ci			val |= GICR_VPROPBASER_4_1_INDIRECT;
26868c2ecf20Sopenharmony_ci		val |= FIELD_PREP(GICR_VPROPBASER_4_1_PAGE_SIZE,
26878c2ecf20Sopenharmony_ci				  FIELD_GET(GITS_BASER_PAGE_SIZE_MASK, baser));
26888c2ecf20Sopenharmony_ci		switch (FIELD_GET(GITS_BASER_PAGE_SIZE_MASK, baser)) {
26898c2ecf20Sopenharmony_ci		case GIC_PAGE_SIZE_64K:
26908c2ecf20Sopenharmony_ci			addr = GITS_BASER_ADDR_48_to_52(baser);
26918c2ecf20Sopenharmony_ci			break;
26928c2ecf20Sopenharmony_ci		default:
26938c2ecf20Sopenharmony_ci			addr = baser & GENMASK_ULL(47, 12);
26948c2ecf20Sopenharmony_ci			break;
26958c2ecf20Sopenharmony_ci		}
26968c2ecf20Sopenharmony_ci		val |= FIELD_PREP(GICR_VPROPBASER_4_1_ADDR, addr >> 12);
26978c2ecf20Sopenharmony_ci		val |= FIELD_PREP(GICR_VPROPBASER_SHAREABILITY_MASK,
26988c2ecf20Sopenharmony_ci				  FIELD_GET(GITS_BASER_SHAREABILITY_MASK, baser));
26998c2ecf20Sopenharmony_ci		val |= FIELD_PREP(GICR_VPROPBASER_INNER_CACHEABILITY_MASK,
27008c2ecf20Sopenharmony_ci				  FIELD_GET(GITS_BASER_INNER_CACHEABILITY_MASK, baser));
27018c2ecf20Sopenharmony_ci		val |= FIELD_PREP(GICR_VPROPBASER_4_1_SIZE, GITS_BASER_NR_PAGES(baser) - 1);
27028c2ecf20Sopenharmony_ci
27038c2ecf20Sopenharmony_ci		return val;
27048c2ecf20Sopenharmony_ci	}
27058c2ecf20Sopenharmony_ci
27068c2ecf20Sopenharmony_ci	return 0;
27078c2ecf20Sopenharmony_ci}
27088c2ecf20Sopenharmony_ci
27098c2ecf20Sopenharmony_cistatic u64 inherit_vpe_l1_table_from_rd(cpumask_t **mask)
27108c2ecf20Sopenharmony_ci{
27118c2ecf20Sopenharmony_ci	u32 aff;
27128c2ecf20Sopenharmony_ci	u64 val;
27138c2ecf20Sopenharmony_ci	int cpu;
27148c2ecf20Sopenharmony_ci
27158c2ecf20Sopenharmony_ci	val = gic_read_typer(gic_data_rdist_rd_base() + GICR_TYPER);
27168c2ecf20Sopenharmony_ci	aff = compute_common_aff(val);
27178c2ecf20Sopenharmony_ci
27188c2ecf20Sopenharmony_ci	for_each_possible_cpu(cpu) {
27198c2ecf20Sopenharmony_ci		void __iomem *base = gic_data_rdist_cpu(cpu)->rd_base;
27208c2ecf20Sopenharmony_ci
27218c2ecf20Sopenharmony_ci		if (!base || cpu == smp_processor_id())
27228c2ecf20Sopenharmony_ci			continue;
27238c2ecf20Sopenharmony_ci
27248c2ecf20Sopenharmony_ci		val = gic_read_typer(base + GICR_TYPER);
27258c2ecf20Sopenharmony_ci		if (aff != compute_common_aff(val))
27268c2ecf20Sopenharmony_ci			continue;
27278c2ecf20Sopenharmony_ci
27288c2ecf20Sopenharmony_ci		/*
27298c2ecf20Sopenharmony_ci		 * At this point, we have a victim. This particular CPU
27308c2ecf20Sopenharmony_ci		 * has already booted, and has an affinity that matches
27318c2ecf20Sopenharmony_ci		 * ours wrt CommonLPIAff. Let's use its own VPROPBASER.
27328c2ecf20Sopenharmony_ci		 * Make sure we don't write the Z bit in that case.
27338c2ecf20Sopenharmony_ci		 */
27348c2ecf20Sopenharmony_ci		val = gicr_read_vpropbaser(base + SZ_128K + GICR_VPROPBASER);
27358c2ecf20Sopenharmony_ci		val &= ~GICR_VPROPBASER_4_1_Z;
27368c2ecf20Sopenharmony_ci
27378c2ecf20Sopenharmony_ci		gic_data_rdist()->vpe_l1_base = gic_data_rdist_cpu(cpu)->vpe_l1_base;
27388c2ecf20Sopenharmony_ci		*mask = gic_data_rdist_cpu(cpu)->vpe_table_mask;
27398c2ecf20Sopenharmony_ci
27408c2ecf20Sopenharmony_ci		return val;
27418c2ecf20Sopenharmony_ci	}
27428c2ecf20Sopenharmony_ci
27438c2ecf20Sopenharmony_ci	return 0;
27448c2ecf20Sopenharmony_ci}
27458c2ecf20Sopenharmony_ci
27468c2ecf20Sopenharmony_cistatic bool allocate_vpe_l2_table(int cpu, u32 id)
27478c2ecf20Sopenharmony_ci{
27488c2ecf20Sopenharmony_ci	void __iomem *base = gic_data_rdist_cpu(cpu)->rd_base;
27498c2ecf20Sopenharmony_ci	unsigned int psz, esz, idx, npg, gpsz;
27508c2ecf20Sopenharmony_ci	u64 val;
27518c2ecf20Sopenharmony_ci	struct page *page;
27528c2ecf20Sopenharmony_ci	__le64 *table;
27538c2ecf20Sopenharmony_ci
27548c2ecf20Sopenharmony_ci	if (!gic_rdists->has_rvpeid)
27558c2ecf20Sopenharmony_ci		return true;
27568c2ecf20Sopenharmony_ci
27578c2ecf20Sopenharmony_ci	/* Skip non-present CPUs */
27588c2ecf20Sopenharmony_ci	if (!base)
27598c2ecf20Sopenharmony_ci		return true;
27608c2ecf20Sopenharmony_ci
27618c2ecf20Sopenharmony_ci	val  = gicr_read_vpropbaser(base + SZ_128K + GICR_VPROPBASER);
27628c2ecf20Sopenharmony_ci
27638c2ecf20Sopenharmony_ci	esz  = FIELD_GET(GICR_VPROPBASER_4_1_ENTRY_SIZE, val) + 1;
27648c2ecf20Sopenharmony_ci	gpsz = FIELD_GET(GICR_VPROPBASER_4_1_PAGE_SIZE, val);
27658c2ecf20Sopenharmony_ci	npg  = FIELD_GET(GICR_VPROPBASER_4_1_SIZE, val) + 1;
27668c2ecf20Sopenharmony_ci
27678c2ecf20Sopenharmony_ci	switch (gpsz) {
27688c2ecf20Sopenharmony_ci	default:
27698c2ecf20Sopenharmony_ci		WARN_ON(1);
27708c2ecf20Sopenharmony_ci		fallthrough;
27718c2ecf20Sopenharmony_ci	case GIC_PAGE_SIZE_4K:
27728c2ecf20Sopenharmony_ci		psz = SZ_4K;
27738c2ecf20Sopenharmony_ci		break;
27748c2ecf20Sopenharmony_ci	case GIC_PAGE_SIZE_16K:
27758c2ecf20Sopenharmony_ci		psz = SZ_16K;
27768c2ecf20Sopenharmony_ci		break;
27778c2ecf20Sopenharmony_ci	case GIC_PAGE_SIZE_64K:
27788c2ecf20Sopenharmony_ci		psz = SZ_64K;
27798c2ecf20Sopenharmony_ci		break;
27808c2ecf20Sopenharmony_ci	}
27818c2ecf20Sopenharmony_ci
27828c2ecf20Sopenharmony_ci	/* Don't allow vpe_id that exceeds single, flat table limit */
27838c2ecf20Sopenharmony_ci	if (!(val & GICR_VPROPBASER_4_1_INDIRECT))
27848c2ecf20Sopenharmony_ci		return (id < (npg * psz / (esz * SZ_8)));
27858c2ecf20Sopenharmony_ci
27868c2ecf20Sopenharmony_ci	/* Compute 1st level table index & check if that exceeds table limit */
27878c2ecf20Sopenharmony_ci	idx = id >> ilog2(psz / (esz * SZ_8));
27888c2ecf20Sopenharmony_ci	if (idx >= (npg * psz / GITS_LVL1_ENTRY_SIZE))
27898c2ecf20Sopenharmony_ci		return false;
27908c2ecf20Sopenharmony_ci
27918c2ecf20Sopenharmony_ci	table = gic_data_rdist_cpu(cpu)->vpe_l1_base;
27928c2ecf20Sopenharmony_ci
27938c2ecf20Sopenharmony_ci	/* Allocate memory for 2nd level table */
27948c2ecf20Sopenharmony_ci	if (!table[idx]) {
27958c2ecf20Sopenharmony_ci		page = alloc_pages(GFP_KERNEL | __GFP_ZERO, get_order(psz));
27968c2ecf20Sopenharmony_ci		if (!page)
27978c2ecf20Sopenharmony_ci			return false;
27988c2ecf20Sopenharmony_ci
27998c2ecf20Sopenharmony_ci		/* Flush Lvl2 table to PoC if hw doesn't support coherency */
28008c2ecf20Sopenharmony_ci		if (!(val & GICR_VPROPBASER_SHAREABILITY_MASK))
28018c2ecf20Sopenharmony_ci			gic_flush_dcache_to_poc(page_address(page), psz);
28028c2ecf20Sopenharmony_ci
28038c2ecf20Sopenharmony_ci		table[idx] = cpu_to_le64(page_to_phys(page) | GITS_BASER_VALID);
28048c2ecf20Sopenharmony_ci
28058c2ecf20Sopenharmony_ci		/* Flush Lvl1 entry to PoC if hw doesn't support coherency */
28068c2ecf20Sopenharmony_ci		if (!(val & GICR_VPROPBASER_SHAREABILITY_MASK))
28078c2ecf20Sopenharmony_ci			gic_flush_dcache_to_poc(table + idx, GITS_LVL1_ENTRY_SIZE);
28088c2ecf20Sopenharmony_ci
28098c2ecf20Sopenharmony_ci		/* Ensure updated table contents are visible to RD hardware */
28108c2ecf20Sopenharmony_ci		dsb(sy);
28118c2ecf20Sopenharmony_ci	}
28128c2ecf20Sopenharmony_ci
28138c2ecf20Sopenharmony_ci	return true;
28148c2ecf20Sopenharmony_ci}
28158c2ecf20Sopenharmony_ci
28168c2ecf20Sopenharmony_cistatic int allocate_vpe_l1_table(void)
28178c2ecf20Sopenharmony_ci{
28188c2ecf20Sopenharmony_ci	void __iomem *vlpi_base = gic_data_rdist_vlpi_base();
28198c2ecf20Sopenharmony_ci	u64 val, gpsz, npg, pa;
28208c2ecf20Sopenharmony_ci	unsigned int psz = SZ_64K;
28218c2ecf20Sopenharmony_ci	unsigned int np, epp, esz;
28228c2ecf20Sopenharmony_ci	struct page *page;
28238c2ecf20Sopenharmony_ci
28248c2ecf20Sopenharmony_ci	if (!gic_rdists->has_rvpeid)
28258c2ecf20Sopenharmony_ci		return 0;
28268c2ecf20Sopenharmony_ci
28278c2ecf20Sopenharmony_ci	/*
28288c2ecf20Sopenharmony_ci	 * if VPENDBASER.Valid is set, disable any previously programmed
28298c2ecf20Sopenharmony_ci	 * VPE by setting PendingLast while clearing Valid. This has the
28308c2ecf20Sopenharmony_ci	 * effect of making sure no doorbell will be generated and we can
28318c2ecf20Sopenharmony_ci	 * then safely clear VPROPBASER.Valid.
28328c2ecf20Sopenharmony_ci	 */
28338c2ecf20Sopenharmony_ci	if (gicr_read_vpendbaser(vlpi_base + GICR_VPENDBASER) & GICR_VPENDBASER_Valid)
28348c2ecf20Sopenharmony_ci		gicr_write_vpendbaser(GICR_VPENDBASER_PendingLast,
28358c2ecf20Sopenharmony_ci				      vlpi_base + GICR_VPENDBASER);
28368c2ecf20Sopenharmony_ci
28378c2ecf20Sopenharmony_ci	/*
28388c2ecf20Sopenharmony_ci	 * If we can inherit the configuration from another RD, let's do
28398c2ecf20Sopenharmony_ci	 * so. Otherwise, we have to go through the allocation process. We
28408c2ecf20Sopenharmony_ci	 * assume that all RDs have the exact same requirements, as
28418c2ecf20Sopenharmony_ci	 * nothing will work otherwise.
28428c2ecf20Sopenharmony_ci	 */
28438c2ecf20Sopenharmony_ci	val = inherit_vpe_l1_table_from_rd(&gic_data_rdist()->vpe_table_mask);
28448c2ecf20Sopenharmony_ci	if (val & GICR_VPROPBASER_4_1_VALID)
28458c2ecf20Sopenharmony_ci		goto out;
28468c2ecf20Sopenharmony_ci
28478c2ecf20Sopenharmony_ci	gic_data_rdist()->vpe_table_mask = kzalloc(sizeof(cpumask_t), GFP_ATOMIC);
28488c2ecf20Sopenharmony_ci	if (!gic_data_rdist()->vpe_table_mask)
28498c2ecf20Sopenharmony_ci		return -ENOMEM;
28508c2ecf20Sopenharmony_ci
28518c2ecf20Sopenharmony_ci	val = inherit_vpe_l1_table_from_its();
28528c2ecf20Sopenharmony_ci	if (val & GICR_VPROPBASER_4_1_VALID)
28538c2ecf20Sopenharmony_ci		goto out;
28548c2ecf20Sopenharmony_ci
28558c2ecf20Sopenharmony_ci	/* First probe the page size */
28568c2ecf20Sopenharmony_ci	val = FIELD_PREP(GICR_VPROPBASER_4_1_PAGE_SIZE, GIC_PAGE_SIZE_64K);
28578c2ecf20Sopenharmony_ci	gicr_write_vpropbaser(val, vlpi_base + GICR_VPROPBASER);
28588c2ecf20Sopenharmony_ci	val = gicr_read_vpropbaser(vlpi_base + GICR_VPROPBASER);
28598c2ecf20Sopenharmony_ci	gpsz = FIELD_GET(GICR_VPROPBASER_4_1_PAGE_SIZE, val);
28608c2ecf20Sopenharmony_ci	esz = FIELD_GET(GICR_VPROPBASER_4_1_ENTRY_SIZE, val);
28618c2ecf20Sopenharmony_ci
28628c2ecf20Sopenharmony_ci	switch (gpsz) {
28638c2ecf20Sopenharmony_ci	default:
28648c2ecf20Sopenharmony_ci		gpsz = GIC_PAGE_SIZE_4K;
28658c2ecf20Sopenharmony_ci		fallthrough;
28668c2ecf20Sopenharmony_ci	case GIC_PAGE_SIZE_4K:
28678c2ecf20Sopenharmony_ci		psz = SZ_4K;
28688c2ecf20Sopenharmony_ci		break;
28698c2ecf20Sopenharmony_ci	case GIC_PAGE_SIZE_16K:
28708c2ecf20Sopenharmony_ci		psz = SZ_16K;
28718c2ecf20Sopenharmony_ci		break;
28728c2ecf20Sopenharmony_ci	case GIC_PAGE_SIZE_64K:
28738c2ecf20Sopenharmony_ci		psz = SZ_64K;
28748c2ecf20Sopenharmony_ci		break;
28758c2ecf20Sopenharmony_ci	}
28768c2ecf20Sopenharmony_ci
28778c2ecf20Sopenharmony_ci	/*
28788c2ecf20Sopenharmony_ci	 * Start populating the register from scratch, including RO fields
28798c2ecf20Sopenharmony_ci	 * (which we want to print in debug cases...)
28808c2ecf20Sopenharmony_ci	 */
28818c2ecf20Sopenharmony_ci	val = 0;
28828c2ecf20Sopenharmony_ci	val |= FIELD_PREP(GICR_VPROPBASER_4_1_PAGE_SIZE, gpsz);
28838c2ecf20Sopenharmony_ci	val |= FIELD_PREP(GICR_VPROPBASER_4_1_ENTRY_SIZE, esz);
28848c2ecf20Sopenharmony_ci
28858c2ecf20Sopenharmony_ci	/* How many entries per GIC page? */
28868c2ecf20Sopenharmony_ci	esz++;
28878c2ecf20Sopenharmony_ci	epp = psz / (esz * SZ_8);
28888c2ecf20Sopenharmony_ci
28898c2ecf20Sopenharmony_ci	/*
28908c2ecf20Sopenharmony_ci	 * If we need more than just a single L1 page, flag the table
28918c2ecf20Sopenharmony_ci	 * as indirect and compute the number of required L1 pages.
28928c2ecf20Sopenharmony_ci	 */
28938c2ecf20Sopenharmony_ci	if (epp < ITS_MAX_VPEID) {
28948c2ecf20Sopenharmony_ci		int nl2;
28958c2ecf20Sopenharmony_ci
28968c2ecf20Sopenharmony_ci		val |= GICR_VPROPBASER_4_1_INDIRECT;
28978c2ecf20Sopenharmony_ci
28988c2ecf20Sopenharmony_ci		/* Number of L2 pages required to cover the VPEID space */
28998c2ecf20Sopenharmony_ci		nl2 = DIV_ROUND_UP(ITS_MAX_VPEID, epp);
29008c2ecf20Sopenharmony_ci
29018c2ecf20Sopenharmony_ci		/* Number of L1 pages to point to the L2 pages */
29028c2ecf20Sopenharmony_ci		npg = DIV_ROUND_UP(nl2 * SZ_8, psz);
29038c2ecf20Sopenharmony_ci	} else {
29048c2ecf20Sopenharmony_ci		npg = 1;
29058c2ecf20Sopenharmony_ci	}
29068c2ecf20Sopenharmony_ci
29078c2ecf20Sopenharmony_ci	val |= FIELD_PREP(GICR_VPROPBASER_4_1_SIZE, npg - 1);
29088c2ecf20Sopenharmony_ci
29098c2ecf20Sopenharmony_ci	/* Right, that's the number of CPU pages we need for L1 */
29108c2ecf20Sopenharmony_ci	np = DIV_ROUND_UP(npg * psz, PAGE_SIZE);
29118c2ecf20Sopenharmony_ci
29128c2ecf20Sopenharmony_ci	pr_debug("np = %d, npg = %lld, psz = %d, epp = %d, esz = %d\n",
29138c2ecf20Sopenharmony_ci		 np, npg, psz, epp, esz);
29148c2ecf20Sopenharmony_ci	page = alloc_pages(GFP_ATOMIC | __GFP_ZERO, get_order(np * PAGE_SIZE));
29158c2ecf20Sopenharmony_ci	if (!page)
29168c2ecf20Sopenharmony_ci		return -ENOMEM;
29178c2ecf20Sopenharmony_ci
29188c2ecf20Sopenharmony_ci	gic_data_rdist()->vpe_l1_base = page_address(page);
29198c2ecf20Sopenharmony_ci	pa = virt_to_phys(page_address(page));
29208c2ecf20Sopenharmony_ci	WARN_ON(!IS_ALIGNED(pa, psz));
29218c2ecf20Sopenharmony_ci
29228c2ecf20Sopenharmony_ci	val |= FIELD_PREP(GICR_VPROPBASER_4_1_ADDR, pa >> 12);
29238c2ecf20Sopenharmony_ci	val |= GICR_VPROPBASER_RaWb;
29248c2ecf20Sopenharmony_ci	val |= GICR_VPROPBASER_InnerShareable;
29258c2ecf20Sopenharmony_ci	val |= GICR_VPROPBASER_4_1_Z;
29268c2ecf20Sopenharmony_ci	val |= GICR_VPROPBASER_4_1_VALID;
29278c2ecf20Sopenharmony_ci
29288c2ecf20Sopenharmony_ciout:
29298c2ecf20Sopenharmony_ci	gicr_write_vpropbaser(val, vlpi_base + GICR_VPROPBASER);
29308c2ecf20Sopenharmony_ci	cpumask_set_cpu(smp_processor_id(), gic_data_rdist()->vpe_table_mask);
29318c2ecf20Sopenharmony_ci
29328c2ecf20Sopenharmony_ci	pr_debug("CPU%d: VPROPBASER = %llx %*pbl\n",
29338c2ecf20Sopenharmony_ci		 smp_processor_id(), val,
29348c2ecf20Sopenharmony_ci		 cpumask_pr_args(gic_data_rdist()->vpe_table_mask));
29358c2ecf20Sopenharmony_ci
29368c2ecf20Sopenharmony_ci	return 0;
29378c2ecf20Sopenharmony_ci}
29388c2ecf20Sopenharmony_ci
29398c2ecf20Sopenharmony_cistatic int its_alloc_collections(struct its_node *its)
29408c2ecf20Sopenharmony_ci{
29418c2ecf20Sopenharmony_ci	int i;
29428c2ecf20Sopenharmony_ci
29438c2ecf20Sopenharmony_ci	its->collections = kcalloc(nr_cpu_ids, sizeof(*its->collections),
29448c2ecf20Sopenharmony_ci				   GFP_KERNEL);
29458c2ecf20Sopenharmony_ci	if (!its->collections)
29468c2ecf20Sopenharmony_ci		return -ENOMEM;
29478c2ecf20Sopenharmony_ci
29488c2ecf20Sopenharmony_ci	for (i = 0; i < nr_cpu_ids; i++)
29498c2ecf20Sopenharmony_ci		its->collections[i].target_address = ~0ULL;
29508c2ecf20Sopenharmony_ci
29518c2ecf20Sopenharmony_ci	return 0;
29528c2ecf20Sopenharmony_ci}
29538c2ecf20Sopenharmony_ci
29548c2ecf20Sopenharmony_cistatic struct page *its_allocate_pending_table(gfp_t gfp_flags)
29558c2ecf20Sopenharmony_ci{
29568c2ecf20Sopenharmony_ci	struct page *pend_page;
29578c2ecf20Sopenharmony_ci
29588c2ecf20Sopenharmony_ci	pend_page = alloc_pages(gfp_flags | __GFP_ZERO,
29598c2ecf20Sopenharmony_ci				get_order(LPI_PENDBASE_SZ));
29608c2ecf20Sopenharmony_ci	if (!pend_page)
29618c2ecf20Sopenharmony_ci		return NULL;
29628c2ecf20Sopenharmony_ci
29638c2ecf20Sopenharmony_ci	/* Make sure the GIC will observe the zero-ed page */
29648c2ecf20Sopenharmony_ci	gic_flush_dcache_to_poc(page_address(pend_page), LPI_PENDBASE_SZ);
29658c2ecf20Sopenharmony_ci
29668c2ecf20Sopenharmony_ci	return pend_page;
29678c2ecf20Sopenharmony_ci}
29688c2ecf20Sopenharmony_ci
29698c2ecf20Sopenharmony_cistatic void its_free_pending_table(struct page *pt)
29708c2ecf20Sopenharmony_ci{
29718c2ecf20Sopenharmony_ci	free_pages((unsigned long)page_address(pt), get_order(LPI_PENDBASE_SZ));
29728c2ecf20Sopenharmony_ci}
29738c2ecf20Sopenharmony_ci
29748c2ecf20Sopenharmony_ci/*
29758c2ecf20Sopenharmony_ci * Booting with kdump and LPIs enabled is generally fine. Any other
29768c2ecf20Sopenharmony_ci * case is wrong in the absence of firmware/EFI support.
29778c2ecf20Sopenharmony_ci */
29788c2ecf20Sopenharmony_cistatic bool enabled_lpis_allowed(void)
29798c2ecf20Sopenharmony_ci{
29808c2ecf20Sopenharmony_ci	phys_addr_t addr;
29818c2ecf20Sopenharmony_ci	u64 val;
29828c2ecf20Sopenharmony_ci
29838c2ecf20Sopenharmony_ci	/* Check whether the property table is in a reserved region */
29848c2ecf20Sopenharmony_ci	val = gicr_read_propbaser(gic_data_rdist_rd_base() + GICR_PROPBASER);
29858c2ecf20Sopenharmony_ci	addr = val & GENMASK_ULL(51, 12);
29868c2ecf20Sopenharmony_ci
29878c2ecf20Sopenharmony_ci	return gic_check_reserved_range(addr, LPI_PROPBASE_SZ);
29888c2ecf20Sopenharmony_ci}
29898c2ecf20Sopenharmony_ci
29908c2ecf20Sopenharmony_cistatic int __init allocate_lpi_tables(void)
29918c2ecf20Sopenharmony_ci{
29928c2ecf20Sopenharmony_ci	u64 val;
29938c2ecf20Sopenharmony_ci	int err, cpu;
29948c2ecf20Sopenharmony_ci
29958c2ecf20Sopenharmony_ci	/*
29968c2ecf20Sopenharmony_ci	 * If LPIs are enabled while we run this from the boot CPU,
29978c2ecf20Sopenharmony_ci	 * flag the RD tables as pre-allocated if the stars do align.
29988c2ecf20Sopenharmony_ci	 */
29998c2ecf20Sopenharmony_ci	val = readl_relaxed(gic_data_rdist_rd_base() + GICR_CTLR);
30008c2ecf20Sopenharmony_ci	if ((val & GICR_CTLR_ENABLE_LPIS) && enabled_lpis_allowed()) {
30018c2ecf20Sopenharmony_ci		gic_rdists->flags |= (RDIST_FLAGS_RD_TABLES_PREALLOCATED |
30028c2ecf20Sopenharmony_ci				      RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING);
30038c2ecf20Sopenharmony_ci		pr_info("GICv3: Using preallocated redistributor tables\n");
30048c2ecf20Sopenharmony_ci	}
30058c2ecf20Sopenharmony_ci
30068c2ecf20Sopenharmony_ci	err = its_setup_lpi_prop_table();
30078c2ecf20Sopenharmony_ci	if (err)
30088c2ecf20Sopenharmony_ci		return err;
30098c2ecf20Sopenharmony_ci
30108c2ecf20Sopenharmony_ci	/*
30118c2ecf20Sopenharmony_ci	 * We allocate all the pending tables anyway, as we may have a
30128c2ecf20Sopenharmony_ci	 * mix of RDs that have had LPIs enabled, and some that
30138c2ecf20Sopenharmony_ci	 * don't. We'll free the unused ones as each CPU comes online.
30148c2ecf20Sopenharmony_ci	 */
30158c2ecf20Sopenharmony_ci	for_each_possible_cpu(cpu) {
30168c2ecf20Sopenharmony_ci		struct page *pend_page;
30178c2ecf20Sopenharmony_ci
30188c2ecf20Sopenharmony_ci		pend_page = its_allocate_pending_table(GFP_NOWAIT);
30198c2ecf20Sopenharmony_ci		if (!pend_page) {
30208c2ecf20Sopenharmony_ci			pr_err("Failed to allocate PENDBASE for CPU%d\n", cpu);
30218c2ecf20Sopenharmony_ci			return -ENOMEM;
30228c2ecf20Sopenharmony_ci		}
30238c2ecf20Sopenharmony_ci
30248c2ecf20Sopenharmony_ci		gic_data_rdist_cpu(cpu)->pend_page = pend_page;
30258c2ecf20Sopenharmony_ci	}
30268c2ecf20Sopenharmony_ci
30278c2ecf20Sopenharmony_ci	return 0;
30288c2ecf20Sopenharmony_ci}
30298c2ecf20Sopenharmony_ci
30308c2ecf20Sopenharmony_cistatic u64 its_clear_vpend_valid(void __iomem *vlpi_base, u64 clr, u64 set)
30318c2ecf20Sopenharmony_ci{
30328c2ecf20Sopenharmony_ci	u32 count = 1000000;	/* 1s! */
30338c2ecf20Sopenharmony_ci	bool clean;
30348c2ecf20Sopenharmony_ci	u64 val;
30358c2ecf20Sopenharmony_ci
30368c2ecf20Sopenharmony_ci	val = gicr_read_vpendbaser(vlpi_base + GICR_VPENDBASER);
30378c2ecf20Sopenharmony_ci	val &= ~GICR_VPENDBASER_Valid;
30388c2ecf20Sopenharmony_ci	val &= ~clr;
30398c2ecf20Sopenharmony_ci	val |= set;
30408c2ecf20Sopenharmony_ci	gicr_write_vpendbaser(val, vlpi_base + GICR_VPENDBASER);
30418c2ecf20Sopenharmony_ci
30428c2ecf20Sopenharmony_ci	do {
30438c2ecf20Sopenharmony_ci		val = gicr_read_vpendbaser(vlpi_base + GICR_VPENDBASER);
30448c2ecf20Sopenharmony_ci		clean = !(val & GICR_VPENDBASER_Dirty);
30458c2ecf20Sopenharmony_ci		if (!clean) {
30468c2ecf20Sopenharmony_ci			count--;
30478c2ecf20Sopenharmony_ci			cpu_relax();
30488c2ecf20Sopenharmony_ci			udelay(1);
30498c2ecf20Sopenharmony_ci		}
30508c2ecf20Sopenharmony_ci	} while (!clean && count);
30518c2ecf20Sopenharmony_ci
30528c2ecf20Sopenharmony_ci	if (unlikely(val & GICR_VPENDBASER_Dirty)) {
30538c2ecf20Sopenharmony_ci		pr_err_ratelimited("ITS virtual pending table not cleaning\n");
30548c2ecf20Sopenharmony_ci		val |= GICR_VPENDBASER_PendingLast;
30558c2ecf20Sopenharmony_ci	}
30568c2ecf20Sopenharmony_ci
30578c2ecf20Sopenharmony_ci	return val;
30588c2ecf20Sopenharmony_ci}
30598c2ecf20Sopenharmony_ci
30608c2ecf20Sopenharmony_cistatic void its_cpu_init_lpis(void)
30618c2ecf20Sopenharmony_ci{
30628c2ecf20Sopenharmony_ci	void __iomem *rbase = gic_data_rdist_rd_base();
30638c2ecf20Sopenharmony_ci	struct page *pend_page;
30648c2ecf20Sopenharmony_ci	phys_addr_t paddr;
30658c2ecf20Sopenharmony_ci	u64 val, tmp;
30668c2ecf20Sopenharmony_ci
30678c2ecf20Sopenharmony_ci	if (gic_data_rdist()->lpi_enabled)
30688c2ecf20Sopenharmony_ci		return;
30698c2ecf20Sopenharmony_ci
30708c2ecf20Sopenharmony_ci	val = readl_relaxed(rbase + GICR_CTLR);
30718c2ecf20Sopenharmony_ci	if ((gic_rdists->flags & RDIST_FLAGS_RD_TABLES_PREALLOCATED) &&
30728c2ecf20Sopenharmony_ci	    (val & GICR_CTLR_ENABLE_LPIS)) {
30738c2ecf20Sopenharmony_ci		/*
30748c2ecf20Sopenharmony_ci		 * Check that we get the same property table on all
30758c2ecf20Sopenharmony_ci		 * RDs. If we don't, this is hopeless.
30768c2ecf20Sopenharmony_ci		 */
30778c2ecf20Sopenharmony_ci		paddr = gicr_read_propbaser(rbase + GICR_PROPBASER);
30788c2ecf20Sopenharmony_ci		paddr &= GENMASK_ULL(51, 12);
30798c2ecf20Sopenharmony_ci		if (WARN_ON(gic_rdists->prop_table_pa != paddr))
30808c2ecf20Sopenharmony_ci			add_taint(TAINT_CRAP, LOCKDEP_STILL_OK);
30818c2ecf20Sopenharmony_ci
30828c2ecf20Sopenharmony_ci		paddr = gicr_read_pendbaser(rbase + GICR_PENDBASER);
30838c2ecf20Sopenharmony_ci		paddr &= GENMASK_ULL(51, 16);
30848c2ecf20Sopenharmony_ci
30858c2ecf20Sopenharmony_ci		WARN_ON(!gic_check_reserved_range(paddr, LPI_PENDBASE_SZ));
30868c2ecf20Sopenharmony_ci		its_free_pending_table(gic_data_rdist()->pend_page);
30878c2ecf20Sopenharmony_ci		gic_data_rdist()->pend_page = NULL;
30888c2ecf20Sopenharmony_ci
30898c2ecf20Sopenharmony_ci		goto out;
30908c2ecf20Sopenharmony_ci	}
30918c2ecf20Sopenharmony_ci
30928c2ecf20Sopenharmony_ci	pend_page = gic_data_rdist()->pend_page;
30938c2ecf20Sopenharmony_ci	paddr = page_to_phys(pend_page);
30948c2ecf20Sopenharmony_ci	WARN_ON(gic_reserve_range(paddr, LPI_PENDBASE_SZ));
30958c2ecf20Sopenharmony_ci
30968c2ecf20Sopenharmony_ci	/* set PROPBASE */
30978c2ecf20Sopenharmony_ci	val = (gic_rdists->prop_table_pa |
30988c2ecf20Sopenharmony_ci	       GICR_PROPBASER_InnerShareable |
30998c2ecf20Sopenharmony_ci	       GICR_PROPBASER_RaWaWb |
31008c2ecf20Sopenharmony_ci	       ((LPI_NRBITS - 1) & GICR_PROPBASER_IDBITS_MASK));
31018c2ecf20Sopenharmony_ci
31028c2ecf20Sopenharmony_ci	gicr_write_propbaser(val, rbase + GICR_PROPBASER);
31038c2ecf20Sopenharmony_ci	tmp = gicr_read_propbaser(rbase + GICR_PROPBASER);
31048c2ecf20Sopenharmony_ci
31058c2ecf20Sopenharmony_ci	if ((tmp ^ val) & GICR_PROPBASER_SHAREABILITY_MASK) {
31068c2ecf20Sopenharmony_ci		if (!(tmp & GICR_PROPBASER_SHAREABILITY_MASK)) {
31078c2ecf20Sopenharmony_ci			/*
31088c2ecf20Sopenharmony_ci			 * The HW reports non-shareable, we must
31098c2ecf20Sopenharmony_ci			 * remove the cacheability attributes as
31108c2ecf20Sopenharmony_ci			 * well.
31118c2ecf20Sopenharmony_ci			 */
31128c2ecf20Sopenharmony_ci			val &= ~(GICR_PROPBASER_SHAREABILITY_MASK |
31138c2ecf20Sopenharmony_ci				 GICR_PROPBASER_CACHEABILITY_MASK);
31148c2ecf20Sopenharmony_ci			val |= GICR_PROPBASER_nC;
31158c2ecf20Sopenharmony_ci			gicr_write_propbaser(val, rbase + GICR_PROPBASER);
31168c2ecf20Sopenharmony_ci		}
31178c2ecf20Sopenharmony_ci		pr_info_once("GIC: using cache flushing for LPI property table\n");
31188c2ecf20Sopenharmony_ci		gic_rdists->flags |= RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING;
31198c2ecf20Sopenharmony_ci	}
31208c2ecf20Sopenharmony_ci
31218c2ecf20Sopenharmony_ci	/* set PENDBASE */
31228c2ecf20Sopenharmony_ci	val = (page_to_phys(pend_page) |
31238c2ecf20Sopenharmony_ci	       GICR_PENDBASER_InnerShareable |
31248c2ecf20Sopenharmony_ci	       GICR_PENDBASER_RaWaWb);
31258c2ecf20Sopenharmony_ci
31268c2ecf20Sopenharmony_ci	gicr_write_pendbaser(val, rbase + GICR_PENDBASER);
31278c2ecf20Sopenharmony_ci	tmp = gicr_read_pendbaser(rbase + GICR_PENDBASER);
31288c2ecf20Sopenharmony_ci
31298c2ecf20Sopenharmony_ci	if (!(tmp & GICR_PENDBASER_SHAREABILITY_MASK)) {
31308c2ecf20Sopenharmony_ci		/*
31318c2ecf20Sopenharmony_ci		 * The HW reports non-shareable, we must remove the
31328c2ecf20Sopenharmony_ci		 * cacheability attributes as well.
31338c2ecf20Sopenharmony_ci		 */
31348c2ecf20Sopenharmony_ci		val &= ~(GICR_PENDBASER_SHAREABILITY_MASK |
31358c2ecf20Sopenharmony_ci			 GICR_PENDBASER_CACHEABILITY_MASK);
31368c2ecf20Sopenharmony_ci		val |= GICR_PENDBASER_nC;
31378c2ecf20Sopenharmony_ci		gicr_write_pendbaser(val, rbase + GICR_PENDBASER);
31388c2ecf20Sopenharmony_ci	}
31398c2ecf20Sopenharmony_ci
31408c2ecf20Sopenharmony_ci	/* Enable LPIs */
31418c2ecf20Sopenharmony_ci	val = readl_relaxed(rbase + GICR_CTLR);
31428c2ecf20Sopenharmony_ci	val |= GICR_CTLR_ENABLE_LPIS;
31438c2ecf20Sopenharmony_ci	writel_relaxed(val, rbase + GICR_CTLR);
31448c2ecf20Sopenharmony_ci
31458c2ecf20Sopenharmony_ci	if (gic_rdists->has_vlpis && !gic_rdists->has_rvpeid) {
31468c2ecf20Sopenharmony_ci		void __iomem *vlpi_base = gic_data_rdist_vlpi_base();
31478c2ecf20Sopenharmony_ci
31488c2ecf20Sopenharmony_ci		/*
31498c2ecf20Sopenharmony_ci		 * It's possible for CPU to receive VLPIs before it is
31508c2ecf20Sopenharmony_ci		 * scheduled as a vPE, especially for the first CPU, and the
31518c2ecf20Sopenharmony_ci		 * VLPI with INTID larger than 2^(IDbits+1) will be considered
31528c2ecf20Sopenharmony_ci		 * as out of range and dropped by GIC.
31538c2ecf20Sopenharmony_ci		 * So we initialize IDbits to known value to avoid VLPI drop.
31548c2ecf20Sopenharmony_ci		 */
31558c2ecf20Sopenharmony_ci		val = (LPI_NRBITS - 1) & GICR_VPROPBASER_IDBITS_MASK;
31568c2ecf20Sopenharmony_ci		pr_debug("GICv4: CPU%d: Init IDbits to 0x%llx for GICR_VPROPBASER\n",
31578c2ecf20Sopenharmony_ci			smp_processor_id(), val);
31588c2ecf20Sopenharmony_ci		gicr_write_vpropbaser(val, vlpi_base + GICR_VPROPBASER);
31598c2ecf20Sopenharmony_ci
31608c2ecf20Sopenharmony_ci		/*
31618c2ecf20Sopenharmony_ci		 * Also clear Valid bit of GICR_VPENDBASER, in case some
31628c2ecf20Sopenharmony_ci		 * ancient programming gets left in and has possibility of
31638c2ecf20Sopenharmony_ci		 * corrupting memory.
31648c2ecf20Sopenharmony_ci		 */
31658c2ecf20Sopenharmony_ci		val = its_clear_vpend_valid(vlpi_base, 0, 0);
31668c2ecf20Sopenharmony_ci	}
31678c2ecf20Sopenharmony_ci
31688c2ecf20Sopenharmony_ci	if (allocate_vpe_l1_table()) {
31698c2ecf20Sopenharmony_ci		/*
31708c2ecf20Sopenharmony_ci		 * If the allocation has failed, we're in massive trouble.
31718c2ecf20Sopenharmony_ci		 * Disable direct injection, and pray that no VM was
31728c2ecf20Sopenharmony_ci		 * already running...
31738c2ecf20Sopenharmony_ci		 */
31748c2ecf20Sopenharmony_ci		gic_rdists->has_rvpeid = false;
31758c2ecf20Sopenharmony_ci		gic_rdists->has_vlpis = false;
31768c2ecf20Sopenharmony_ci	}
31778c2ecf20Sopenharmony_ci
31788c2ecf20Sopenharmony_ci	/* Make sure the GIC has seen the above */
31798c2ecf20Sopenharmony_ci	dsb(sy);
31808c2ecf20Sopenharmony_ciout:
31818c2ecf20Sopenharmony_ci	gic_data_rdist()->lpi_enabled = true;
31828c2ecf20Sopenharmony_ci	pr_info("GICv3: CPU%d: using %s LPI pending table @%pa\n",
31838c2ecf20Sopenharmony_ci		smp_processor_id(),
31848c2ecf20Sopenharmony_ci		gic_data_rdist()->pend_page ? "allocated" : "reserved",
31858c2ecf20Sopenharmony_ci		&paddr);
31868c2ecf20Sopenharmony_ci}
31878c2ecf20Sopenharmony_ci
31888c2ecf20Sopenharmony_cistatic void its_cpu_init_collection(struct its_node *its)
31898c2ecf20Sopenharmony_ci{
31908c2ecf20Sopenharmony_ci	int cpu = smp_processor_id();
31918c2ecf20Sopenharmony_ci	u64 target;
31928c2ecf20Sopenharmony_ci
31938c2ecf20Sopenharmony_ci	/* avoid cross node collections and its mapping */
31948c2ecf20Sopenharmony_ci	if (its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_23144) {
31958c2ecf20Sopenharmony_ci		struct device_node *cpu_node;
31968c2ecf20Sopenharmony_ci
31978c2ecf20Sopenharmony_ci		cpu_node = of_get_cpu_node(cpu, NULL);
31988c2ecf20Sopenharmony_ci		if (its->numa_node != NUMA_NO_NODE &&
31998c2ecf20Sopenharmony_ci			its->numa_node != of_node_to_nid(cpu_node))
32008c2ecf20Sopenharmony_ci			return;
32018c2ecf20Sopenharmony_ci	}
32028c2ecf20Sopenharmony_ci
32038c2ecf20Sopenharmony_ci	/*
32048c2ecf20Sopenharmony_ci	 * We now have to bind each collection to its target
32058c2ecf20Sopenharmony_ci	 * redistributor.
32068c2ecf20Sopenharmony_ci	 */
32078c2ecf20Sopenharmony_ci	if (gic_read_typer(its->base + GITS_TYPER) & GITS_TYPER_PTA) {
32088c2ecf20Sopenharmony_ci		/*
32098c2ecf20Sopenharmony_ci		 * This ITS wants the physical address of the
32108c2ecf20Sopenharmony_ci		 * redistributor.
32118c2ecf20Sopenharmony_ci		 */
32128c2ecf20Sopenharmony_ci		target = gic_data_rdist()->phys_base;
32138c2ecf20Sopenharmony_ci	} else {
32148c2ecf20Sopenharmony_ci		/* This ITS wants a linear CPU number. */
32158c2ecf20Sopenharmony_ci		target = gic_read_typer(gic_data_rdist_rd_base() + GICR_TYPER);
32168c2ecf20Sopenharmony_ci		target = GICR_TYPER_CPU_NUMBER(target) << 16;
32178c2ecf20Sopenharmony_ci	}
32188c2ecf20Sopenharmony_ci
32198c2ecf20Sopenharmony_ci	/* Perform collection mapping */
32208c2ecf20Sopenharmony_ci	its->collections[cpu].target_address = target;
32218c2ecf20Sopenharmony_ci	its->collections[cpu].col_id = cpu;
32228c2ecf20Sopenharmony_ci
32238c2ecf20Sopenharmony_ci	its_send_mapc(its, &its->collections[cpu], 1);
32248c2ecf20Sopenharmony_ci	its_send_invall(its, &its->collections[cpu]);
32258c2ecf20Sopenharmony_ci}
32268c2ecf20Sopenharmony_ci
32278c2ecf20Sopenharmony_cistatic void its_cpu_init_collections(void)
32288c2ecf20Sopenharmony_ci{
32298c2ecf20Sopenharmony_ci	struct its_node *its;
32308c2ecf20Sopenharmony_ci
32318c2ecf20Sopenharmony_ci	raw_spin_lock(&its_lock);
32328c2ecf20Sopenharmony_ci
32338c2ecf20Sopenharmony_ci	list_for_each_entry(its, &its_nodes, entry)
32348c2ecf20Sopenharmony_ci		its_cpu_init_collection(its);
32358c2ecf20Sopenharmony_ci
32368c2ecf20Sopenharmony_ci	raw_spin_unlock(&its_lock);
32378c2ecf20Sopenharmony_ci}
32388c2ecf20Sopenharmony_ci
32398c2ecf20Sopenharmony_cistatic struct its_device *its_find_device(struct its_node *its, u32 dev_id)
32408c2ecf20Sopenharmony_ci{
32418c2ecf20Sopenharmony_ci	struct its_device *its_dev = NULL, *tmp;
32428c2ecf20Sopenharmony_ci	unsigned long flags;
32438c2ecf20Sopenharmony_ci
32448c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&its->lock, flags);
32458c2ecf20Sopenharmony_ci
32468c2ecf20Sopenharmony_ci	list_for_each_entry(tmp, &its->its_device_list, entry) {
32478c2ecf20Sopenharmony_ci		if (tmp->device_id == dev_id) {
32488c2ecf20Sopenharmony_ci			its_dev = tmp;
32498c2ecf20Sopenharmony_ci			break;
32508c2ecf20Sopenharmony_ci		}
32518c2ecf20Sopenharmony_ci	}
32528c2ecf20Sopenharmony_ci
32538c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&its->lock, flags);
32548c2ecf20Sopenharmony_ci
32558c2ecf20Sopenharmony_ci	return its_dev;
32568c2ecf20Sopenharmony_ci}
32578c2ecf20Sopenharmony_ci
32588c2ecf20Sopenharmony_cistatic struct its_baser *its_get_baser(struct its_node *its, u32 type)
32598c2ecf20Sopenharmony_ci{
32608c2ecf20Sopenharmony_ci	int i;
32618c2ecf20Sopenharmony_ci
32628c2ecf20Sopenharmony_ci	for (i = 0; i < GITS_BASER_NR_REGS; i++) {
32638c2ecf20Sopenharmony_ci		if (GITS_BASER_TYPE(its->tables[i].val) == type)
32648c2ecf20Sopenharmony_ci			return &its->tables[i];
32658c2ecf20Sopenharmony_ci	}
32668c2ecf20Sopenharmony_ci
32678c2ecf20Sopenharmony_ci	return NULL;
32688c2ecf20Sopenharmony_ci}
32698c2ecf20Sopenharmony_ci
32708c2ecf20Sopenharmony_cistatic bool its_alloc_table_entry(struct its_node *its,
32718c2ecf20Sopenharmony_ci				  struct its_baser *baser, u32 id)
32728c2ecf20Sopenharmony_ci{
32738c2ecf20Sopenharmony_ci	struct page *page;
32748c2ecf20Sopenharmony_ci	u32 esz, idx;
32758c2ecf20Sopenharmony_ci	__le64 *table;
32768c2ecf20Sopenharmony_ci
32778c2ecf20Sopenharmony_ci	/* Don't allow device id that exceeds single, flat table limit */
32788c2ecf20Sopenharmony_ci	esz = GITS_BASER_ENTRY_SIZE(baser->val);
32798c2ecf20Sopenharmony_ci	if (!(baser->val & GITS_BASER_INDIRECT))
32808c2ecf20Sopenharmony_ci		return (id < (PAGE_ORDER_TO_SIZE(baser->order) / esz));
32818c2ecf20Sopenharmony_ci
32828c2ecf20Sopenharmony_ci	/* Compute 1st level table index & check if that exceeds table limit */
32838c2ecf20Sopenharmony_ci	idx = id >> ilog2(baser->psz / esz);
32848c2ecf20Sopenharmony_ci	if (idx >= (PAGE_ORDER_TO_SIZE(baser->order) / GITS_LVL1_ENTRY_SIZE))
32858c2ecf20Sopenharmony_ci		return false;
32868c2ecf20Sopenharmony_ci
32878c2ecf20Sopenharmony_ci	table = baser->base;
32888c2ecf20Sopenharmony_ci
32898c2ecf20Sopenharmony_ci	/* Allocate memory for 2nd level table */
32908c2ecf20Sopenharmony_ci	if (!table[idx]) {
32918c2ecf20Sopenharmony_ci		page = alloc_pages_node(its->numa_node, GFP_KERNEL | __GFP_ZERO,
32928c2ecf20Sopenharmony_ci					get_order(baser->psz));
32938c2ecf20Sopenharmony_ci		if (!page)
32948c2ecf20Sopenharmony_ci			return false;
32958c2ecf20Sopenharmony_ci
32968c2ecf20Sopenharmony_ci		/* Flush Lvl2 table to PoC if hw doesn't support coherency */
32978c2ecf20Sopenharmony_ci		if (!(baser->val & GITS_BASER_SHAREABILITY_MASK))
32988c2ecf20Sopenharmony_ci			gic_flush_dcache_to_poc(page_address(page), baser->psz);
32998c2ecf20Sopenharmony_ci
33008c2ecf20Sopenharmony_ci		table[idx] = cpu_to_le64(page_to_phys(page) | GITS_BASER_VALID);
33018c2ecf20Sopenharmony_ci
33028c2ecf20Sopenharmony_ci		/* Flush Lvl1 entry to PoC if hw doesn't support coherency */
33038c2ecf20Sopenharmony_ci		if (!(baser->val & GITS_BASER_SHAREABILITY_MASK))
33048c2ecf20Sopenharmony_ci			gic_flush_dcache_to_poc(table + idx, GITS_LVL1_ENTRY_SIZE);
33058c2ecf20Sopenharmony_ci
33068c2ecf20Sopenharmony_ci		/* Ensure updated table contents are visible to ITS hardware */
33078c2ecf20Sopenharmony_ci		dsb(sy);
33088c2ecf20Sopenharmony_ci	}
33098c2ecf20Sopenharmony_ci
33108c2ecf20Sopenharmony_ci	return true;
33118c2ecf20Sopenharmony_ci}
33128c2ecf20Sopenharmony_ci
33138c2ecf20Sopenharmony_cistatic bool its_alloc_device_table(struct its_node *its, u32 dev_id)
33148c2ecf20Sopenharmony_ci{
33158c2ecf20Sopenharmony_ci	struct its_baser *baser;
33168c2ecf20Sopenharmony_ci
33178c2ecf20Sopenharmony_ci	baser = its_get_baser(its, GITS_BASER_TYPE_DEVICE);
33188c2ecf20Sopenharmony_ci
33198c2ecf20Sopenharmony_ci	/* Don't allow device id that exceeds ITS hardware limit */
33208c2ecf20Sopenharmony_ci	if (!baser)
33218c2ecf20Sopenharmony_ci		return (ilog2(dev_id) < device_ids(its));
33228c2ecf20Sopenharmony_ci
33238c2ecf20Sopenharmony_ci	return its_alloc_table_entry(its, baser, dev_id);
33248c2ecf20Sopenharmony_ci}
33258c2ecf20Sopenharmony_ci
33268c2ecf20Sopenharmony_cistatic bool its_alloc_vpe_table(u32 vpe_id)
33278c2ecf20Sopenharmony_ci{
33288c2ecf20Sopenharmony_ci	struct its_node *its;
33298c2ecf20Sopenharmony_ci	int cpu;
33308c2ecf20Sopenharmony_ci
33318c2ecf20Sopenharmony_ci	/*
33328c2ecf20Sopenharmony_ci	 * Make sure the L2 tables are allocated on *all* v4 ITSs. We
33338c2ecf20Sopenharmony_ci	 * could try and only do it on ITSs corresponding to devices
33348c2ecf20Sopenharmony_ci	 * that have interrupts targeted at this VPE, but the
33358c2ecf20Sopenharmony_ci	 * complexity becomes crazy (and you have tons of memory
33368c2ecf20Sopenharmony_ci	 * anyway, right?).
33378c2ecf20Sopenharmony_ci	 */
33388c2ecf20Sopenharmony_ci	list_for_each_entry(its, &its_nodes, entry) {
33398c2ecf20Sopenharmony_ci		struct its_baser *baser;
33408c2ecf20Sopenharmony_ci
33418c2ecf20Sopenharmony_ci		if (!is_v4(its))
33428c2ecf20Sopenharmony_ci			continue;
33438c2ecf20Sopenharmony_ci
33448c2ecf20Sopenharmony_ci		baser = its_get_baser(its, GITS_BASER_TYPE_VCPU);
33458c2ecf20Sopenharmony_ci		if (!baser)
33468c2ecf20Sopenharmony_ci			return false;
33478c2ecf20Sopenharmony_ci
33488c2ecf20Sopenharmony_ci		if (!its_alloc_table_entry(its, baser, vpe_id))
33498c2ecf20Sopenharmony_ci			return false;
33508c2ecf20Sopenharmony_ci	}
33518c2ecf20Sopenharmony_ci
33528c2ecf20Sopenharmony_ci	/* Non v4.1? No need to iterate RDs and go back early. */
33538c2ecf20Sopenharmony_ci	if (!gic_rdists->has_rvpeid)
33548c2ecf20Sopenharmony_ci		return true;
33558c2ecf20Sopenharmony_ci
33568c2ecf20Sopenharmony_ci	/*
33578c2ecf20Sopenharmony_ci	 * Make sure the L2 tables are allocated for all copies of
33588c2ecf20Sopenharmony_ci	 * the L1 table on *all* v4.1 RDs.
33598c2ecf20Sopenharmony_ci	 */
33608c2ecf20Sopenharmony_ci	for_each_possible_cpu(cpu) {
33618c2ecf20Sopenharmony_ci		if (!allocate_vpe_l2_table(cpu, vpe_id))
33628c2ecf20Sopenharmony_ci			return false;
33638c2ecf20Sopenharmony_ci	}
33648c2ecf20Sopenharmony_ci
33658c2ecf20Sopenharmony_ci	return true;
33668c2ecf20Sopenharmony_ci}
33678c2ecf20Sopenharmony_ci
33688c2ecf20Sopenharmony_cistatic struct its_device *its_create_device(struct its_node *its, u32 dev_id,
33698c2ecf20Sopenharmony_ci					    int nvecs, bool alloc_lpis)
33708c2ecf20Sopenharmony_ci{
33718c2ecf20Sopenharmony_ci	struct its_device *dev;
33728c2ecf20Sopenharmony_ci	unsigned long *lpi_map = NULL;
33738c2ecf20Sopenharmony_ci	unsigned long flags;
33748c2ecf20Sopenharmony_ci	u16 *col_map = NULL;
33758c2ecf20Sopenharmony_ci	void *itt;
33768c2ecf20Sopenharmony_ci	int lpi_base;
33778c2ecf20Sopenharmony_ci	int nr_lpis;
33788c2ecf20Sopenharmony_ci	int nr_ites;
33798c2ecf20Sopenharmony_ci	int sz;
33808c2ecf20Sopenharmony_ci
33818c2ecf20Sopenharmony_ci	if (!its_alloc_device_table(its, dev_id))
33828c2ecf20Sopenharmony_ci		return NULL;
33838c2ecf20Sopenharmony_ci
33848c2ecf20Sopenharmony_ci	if (WARN_ON(!is_power_of_2(nvecs)))
33858c2ecf20Sopenharmony_ci		nvecs = roundup_pow_of_two(nvecs);
33868c2ecf20Sopenharmony_ci
33878c2ecf20Sopenharmony_ci	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
33888c2ecf20Sopenharmony_ci	/*
33898c2ecf20Sopenharmony_ci	 * Even if the device wants a single LPI, the ITT must be
33908c2ecf20Sopenharmony_ci	 * sized as a power of two (and you need at least one bit...).
33918c2ecf20Sopenharmony_ci	 */
33928c2ecf20Sopenharmony_ci	nr_ites = max(2, nvecs);
33938c2ecf20Sopenharmony_ci	sz = nr_ites * (FIELD_GET(GITS_TYPER_ITT_ENTRY_SIZE, its->typer) + 1);
33948c2ecf20Sopenharmony_ci	sz = max(sz, ITS_ITT_ALIGN) + ITS_ITT_ALIGN - 1;
33958c2ecf20Sopenharmony_ci	itt = kzalloc_node(sz, GFP_KERNEL, its->numa_node);
33968c2ecf20Sopenharmony_ci	if (alloc_lpis) {
33978c2ecf20Sopenharmony_ci		lpi_map = its_lpi_alloc(nvecs, &lpi_base, &nr_lpis);
33988c2ecf20Sopenharmony_ci		if (lpi_map)
33998c2ecf20Sopenharmony_ci			col_map = kcalloc(nr_lpis, sizeof(*col_map),
34008c2ecf20Sopenharmony_ci					  GFP_KERNEL);
34018c2ecf20Sopenharmony_ci	} else {
34028c2ecf20Sopenharmony_ci		col_map = kcalloc(nr_ites, sizeof(*col_map), GFP_KERNEL);
34038c2ecf20Sopenharmony_ci		nr_lpis = 0;
34048c2ecf20Sopenharmony_ci		lpi_base = 0;
34058c2ecf20Sopenharmony_ci	}
34068c2ecf20Sopenharmony_ci
34078c2ecf20Sopenharmony_ci	if (!dev || !itt ||  !col_map || (!lpi_map && alloc_lpis)) {
34088c2ecf20Sopenharmony_ci		kfree(dev);
34098c2ecf20Sopenharmony_ci		kfree(itt);
34108c2ecf20Sopenharmony_ci		kfree(lpi_map);
34118c2ecf20Sopenharmony_ci		kfree(col_map);
34128c2ecf20Sopenharmony_ci		return NULL;
34138c2ecf20Sopenharmony_ci	}
34148c2ecf20Sopenharmony_ci
34158c2ecf20Sopenharmony_ci	gic_flush_dcache_to_poc(itt, sz);
34168c2ecf20Sopenharmony_ci
34178c2ecf20Sopenharmony_ci	dev->its = its;
34188c2ecf20Sopenharmony_ci	dev->itt = itt;
34198c2ecf20Sopenharmony_ci	dev->nr_ites = nr_ites;
34208c2ecf20Sopenharmony_ci	dev->event_map.lpi_map = lpi_map;
34218c2ecf20Sopenharmony_ci	dev->event_map.col_map = col_map;
34228c2ecf20Sopenharmony_ci	dev->event_map.lpi_base = lpi_base;
34238c2ecf20Sopenharmony_ci	dev->event_map.nr_lpis = nr_lpis;
34248c2ecf20Sopenharmony_ci	raw_spin_lock_init(&dev->event_map.vlpi_lock);
34258c2ecf20Sopenharmony_ci	dev->device_id = dev_id;
34268c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&dev->entry);
34278c2ecf20Sopenharmony_ci
34288c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&its->lock, flags);
34298c2ecf20Sopenharmony_ci	list_add(&dev->entry, &its->its_device_list);
34308c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&its->lock, flags);
34318c2ecf20Sopenharmony_ci
34328c2ecf20Sopenharmony_ci	/* Map device to its ITT */
34338c2ecf20Sopenharmony_ci	its_send_mapd(dev, 1);
34348c2ecf20Sopenharmony_ci
34358c2ecf20Sopenharmony_ci	return dev;
34368c2ecf20Sopenharmony_ci}
34378c2ecf20Sopenharmony_ci
34388c2ecf20Sopenharmony_cistatic void its_free_device(struct its_device *its_dev)
34398c2ecf20Sopenharmony_ci{
34408c2ecf20Sopenharmony_ci	unsigned long flags;
34418c2ecf20Sopenharmony_ci
34428c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&its_dev->its->lock, flags);
34438c2ecf20Sopenharmony_ci	list_del(&its_dev->entry);
34448c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&its_dev->its->lock, flags);
34458c2ecf20Sopenharmony_ci	kfree(its_dev->event_map.col_map);
34468c2ecf20Sopenharmony_ci	kfree(its_dev->itt);
34478c2ecf20Sopenharmony_ci	kfree(its_dev);
34488c2ecf20Sopenharmony_ci}
34498c2ecf20Sopenharmony_ci
34508c2ecf20Sopenharmony_cistatic int its_alloc_device_irq(struct its_device *dev, int nvecs, irq_hw_number_t *hwirq)
34518c2ecf20Sopenharmony_ci{
34528c2ecf20Sopenharmony_ci	int idx;
34538c2ecf20Sopenharmony_ci
34548c2ecf20Sopenharmony_ci	/* Find a free LPI region in lpi_map and allocate them. */
34558c2ecf20Sopenharmony_ci	idx = bitmap_find_free_region(dev->event_map.lpi_map,
34568c2ecf20Sopenharmony_ci				      dev->event_map.nr_lpis,
34578c2ecf20Sopenharmony_ci				      get_count_order(nvecs));
34588c2ecf20Sopenharmony_ci	if (idx < 0)
34598c2ecf20Sopenharmony_ci		return -ENOSPC;
34608c2ecf20Sopenharmony_ci
34618c2ecf20Sopenharmony_ci	*hwirq = dev->event_map.lpi_base + idx;
34628c2ecf20Sopenharmony_ci
34638c2ecf20Sopenharmony_ci	return 0;
34648c2ecf20Sopenharmony_ci}
34658c2ecf20Sopenharmony_ci
34668c2ecf20Sopenharmony_cistatic int its_msi_prepare(struct irq_domain *domain, struct device *dev,
34678c2ecf20Sopenharmony_ci			   int nvec, msi_alloc_info_t *info)
34688c2ecf20Sopenharmony_ci{
34698c2ecf20Sopenharmony_ci	struct its_node *its;
34708c2ecf20Sopenharmony_ci	struct its_device *its_dev;
34718c2ecf20Sopenharmony_ci	struct msi_domain_info *msi_info;
34728c2ecf20Sopenharmony_ci	u32 dev_id;
34738c2ecf20Sopenharmony_ci	int err = 0;
34748c2ecf20Sopenharmony_ci
34758c2ecf20Sopenharmony_ci	/*
34768c2ecf20Sopenharmony_ci	 * We ignore "dev" entirely, and rely on the dev_id that has
34778c2ecf20Sopenharmony_ci	 * been passed via the scratchpad. This limits this domain's
34788c2ecf20Sopenharmony_ci	 * usefulness to upper layers that definitely know that they
34798c2ecf20Sopenharmony_ci	 * are built on top of the ITS.
34808c2ecf20Sopenharmony_ci	 */
34818c2ecf20Sopenharmony_ci	dev_id = info->scratchpad[0].ul;
34828c2ecf20Sopenharmony_ci
34838c2ecf20Sopenharmony_ci	msi_info = msi_get_domain_info(domain);
34848c2ecf20Sopenharmony_ci	its = msi_info->data;
34858c2ecf20Sopenharmony_ci
34868c2ecf20Sopenharmony_ci	if (!gic_rdists->has_direct_lpi &&
34878c2ecf20Sopenharmony_ci	    vpe_proxy.dev &&
34888c2ecf20Sopenharmony_ci	    vpe_proxy.dev->its == its &&
34898c2ecf20Sopenharmony_ci	    dev_id == vpe_proxy.dev->device_id) {
34908c2ecf20Sopenharmony_ci		/* Bad luck. Get yourself a better implementation */
34918c2ecf20Sopenharmony_ci		WARN_ONCE(1, "DevId %x clashes with GICv4 VPE proxy device\n",
34928c2ecf20Sopenharmony_ci			  dev_id);
34938c2ecf20Sopenharmony_ci		return -EINVAL;
34948c2ecf20Sopenharmony_ci	}
34958c2ecf20Sopenharmony_ci
34968c2ecf20Sopenharmony_ci	mutex_lock(&its->dev_alloc_lock);
34978c2ecf20Sopenharmony_ci	its_dev = its_find_device(its, dev_id);
34988c2ecf20Sopenharmony_ci	if (its_dev) {
34998c2ecf20Sopenharmony_ci		/*
35008c2ecf20Sopenharmony_ci		 * We already have seen this ID, probably through
35018c2ecf20Sopenharmony_ci		 * another alias (PCI bridge of some sort). No need to
35028c2ecf20Sopenharmony_ci		 * create the device.
35038c2ecf20Sopenharmony_ci		 */
35048c2ecf20Sopenharmony_ci		its_dev->shared = true;
35058c2ecf20Sopenharmony_ci		pr_debug("Reusing ITT for devID %x\n", dev_id);
35068c2ecf20Sopenharmony_ci		goto out;
35078c2ecf20Sopenharmony_ci	}
35088c2ecf20Sopenharmony_ci
35098c2ecf20Sopenharmony_ci	its_dev = its_create_device(its, dev_id, nvec, true);
35108c2ecf20Sopenharmony_ci	if (!its_dev) {
35118c2ecf20Sopenharmony_ci		err = -ENOMEM;
35128c2ecf20Sopenharmony_ci		goto out;
35138c2ecf20Sopenharmony_ci	}
35148c2ecf20Sopenharmony_ci
35158c2ecf20Sopenharmony_ci	pr_debug("ITT %d entries, %d bits\n", nvec, ilog2(nvec));
35168c2ecf20Sopenharmony_ciout:
35178c2ecf20Sopenharmony_ci	mutex_unlock(&its->dev_alloc_lock);
35188c2ecf20Sopenharmony_ci	info->scratchpad[0].ptr = its_dev;
35198c2ecf20Sopenharmony_ci	return err;
35208c2ecf20Sopenharmony_ci}
35218c2ecf20Sopenharmony_ci
35228c2ecf20Sopenharmony_cistatic struct msi_domain_ops its_msi_domain_ops = {
35238c2ecf20Sopenharmony_ci	.msi_prepare	= its_msi_prepare,
35248c2ecf20Sopenharmony_ci};
35258c2ecf20Sopenharmony_ci
35268c2ecf20Sopenharmony_cistatic int its_irq_gic_domain_alloc(struct irq_domain *domain,
35278c2ecf20Sopenharmony_ci				    unsigned int virq,
35288c2ecf20Sopenharmony_ci				    irq_hw_number_t hwirq)
35298c2ecf20Sopenharmony_ci{
35308c2ecf20Sopenharmony_ci	struct irq_fwspec fwspec;
35318c2ecf20Sopenharmony_ci
35328c2ecf20Sopenharmony_ci	if (irq_domain_get_of_node(domain->parent)) {
35338c2ecf20Sopenharmony_ci		fwspec.fwnode = domain->parent->fwnode;
35348c2ecf20Sopenharmony_ci		fwspec.param_count = 3;
35358c2ecf20Sopenharmony_ci		fwspec.param[0] = GIC_IRQ_TYPE_LPI;
35368c2ecf20Sopenharmony_ci		fwspec.param[1] = hwirq;
35378c2ecf20Sopenharmony_ci		fwspec.param[2] = IRQ_TYPE_EDGE_RISING;
35388c2ecf20Sopenharmony_ci	} else if (is_fwnode_irqchip(domain->parent->fwnode)) {
35398c2ecf20Sopenharmony_ci		fwspec.fwnode = domain->parent->fwnode;
35408c2ecf20Sopenharmony_ci		fwspec.param_count = 2;
35418c2ecf20Sopenharmony_ci		fwspec.param[0] = hwirq;
35428c2ecf20Sopenharmony_ci		fwspec.param[1] = IRQ_TYPE_EDGE_RISING;
35438c2ecf20Sopenharmony_ci	} else {
35448c2ecf20Sopenharmony_ci		return -EINVAL;
35458c2ecf20Sopenharmony_ci	}
35468c2ecf20Sopenharmony_ci
35478c2ecf20Sopenharmony_ci	return irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
35488c2ecf20Sopenharmony_ci}
35498c2ecf20Sopenharmony_ci
35508c2ecf20Sopenharmony_cistatic int its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
35518c2ecf20Sopenharmony_ci				unsigned int nr_irqs, void *args)
35528c2ecf20Sopenharmony_ci{
35538c2ecf20Sopenharmony_ci	msi_alloc_info_t *info = args;
35548c2ecf20Sopenharmony_ci	struct its_device *its_dev = info->scratchpad[0].ptr;
35558c2ecf20Sopenharmony_ci	struct its_node *its = its_dev->its;
35568c2ecf20Sopenharmony_ci	struct irq_data *irqd;
35578c2ecf20Sopenharmony_ci	irq_hw_number_t hwirq;
35588c2ecf20Sopenharmony_ci	int err;
35598c2ecf20Sopenharmony_ci	int i;
35608c2ecf20Sopenharmony_ci
35618c2ecf20Sopenharmony_ci	err = its_alloc_device_irq(its_dev, nr_irqs, &hwirq);
35628c2ecf20Sopenharmony_ci	if (err)
35638c2ecf20Sopenharmony_ci		return err;
35648c2ecf20Sopenharmony_ci
35658c2ecf20Sopenharmony_ci	err = iommu_dma_prepare_msi(info->desc, its->get_msi_base(its_dev));
35668c2ecf20Sopenharmony_ci	if (err)
35678c2ecf20Sopenharmony_ci		return err;
35688c2ecf20Sopenharmony_ci
35698c2ecf20Sopenharmony_ci	for (i = 0; i < nr_irqs; i++) {
35708c2ecf20Sopenharmony_ci		err = its_irq_gic_domain_alloc(domain, virq + i, hwirq + i);
35718c2ecf20Sopenharmony_ci		if (err)
35728c2ecf20Sopenharmony_ci			return err;
35738c2ecf20Sopenharmony_ci
35748c2ecf20Sopenharmony_ci		irq_domain_set_hwirq_and_chip(domain, virq + i,
35758c2ecf20Sopenharmony_ci					      hwirq + i, &its_irq_chip, its_dev);
35768c2ecf20Sopenharmony_ci		irqd = irq_get_irq_data(virq + i);
35778c2ecf20Sopenharmony_ci		irqd_set_single_target(irqd);
35788c2ecf20Sopenharmony_ci		irqd_set_affinity_on_activate(irqd);
35798c2ecf20Sopenharmony_ci		pr_debug("ID:%d pID:%d vID:%d\n",
35808c2ecf20Sopenharmony_ci			 (int)(hwirq + i - its_dev->event_map.lpi_base),
35818c2ecf20Sopenharmony_ci			 (int)(hwirq + i), virq + i);
35828c2ecf20Sopenharmony_ci	}
35838c2ecf20Sopenharmony_ci
35848c2ecf20Sopenharmony_ci	return 0;
35858c2ecf20Sopenharmony_ci}
35868c2ecf20Sopenharmony_ci
35878c2ecf20Sopenharmony_cistatic int its_irq_domain_activate(struct irq_domain *domain,
35888c2ecf20Sopenharmony_ci				   struct irq_data *d, bool reserve)
35898c2ecf20Sopenharmony_ci{
35908c2ecf20Sopenharmony_ci	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
35918c2ecf20Sopenharmony_ci	u32 event = its_get_event_id(d);
35928c2ecf20Sopenharmony_ci	int cpu;
35938c2ecf20Sopenharmony_ci
35948c2ecf20Sopenharmony_ci	cpu = its_select_cpu(d, cpu_online_mask);
35958c2ecf20Sopenharmony_ci	if (cpu < 0 || cpu >= nr_cpu_ids)
35968c2ecf20Sopenharmony_ci		return -EINVAL;
35978c2ecf20Sopenharmony_ci
35988c2ecf20Sopenharmony_ci	its_inc_lpi_count(d, cpu);
35998c2ecf20Sopenharmony_ci	its_dev->event_map.col_map[event] = cpu;
36008c2ecf20Sopenharmony_ci	irq_data_update_effective_affinity(d, cpumask_of(cpu));
36018c2ecf20Sopenharmony_ci
36028c2ecf20Sopenharmony_ci	/* Map the GIC IRQ and event to the device */
36038c2ecf20Sopenharmony_ci	its_send_mapti(its_dev, d->hwirq, event);
36048c2ecf20Sopenharmony_ci	return 0;
36058c2ecf20Sopenharmony_ci}
36068c2ecf20Sopenharmony_ci
36078c2ecf20Sopenharmony_cistatic void its_irq_domain_deactivate(struct irq_domain *domain,
36088c2ecf20Sopenharmony_ci				      struct irq_data *d)
36098c2ecf20Sopenharmony_ci{
36108c2ecf20Sopenharmony_ci	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
36118c2ecf20Sopenharmony_ci	u32 event = its_get_event_id(d);
36128c2ecf20Sopenharmony_ci
36138c2ecf20Sopenharmony_ci	its_dec_lpi_count(d, its_dev->event_map.col_map[event]);
36148c2ecf20Sopenharmony_ci	/* Stop the delivery of interrupts */
36158c2ecf20Sopenharmony_ci	its_send_discard(its_dev, event);
36168c2ecf20Sopenharmony_ci}
36178c2ecf20Sopenharmony_ci
36188c2ecf20Sopenharmony_cistatic void its_irq_domain_free(struct irq_domain *domain, unsigned int virq,
36198c2ecf20Sopenharmony_ci				unsigned int nr_irqs)
36208c2ecf20Sopenharmony_ci{
36218c2ecf20Sopenharmony_ci	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
36228c2ecf20Sopenharmony_ci	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
36238c2ecf20Sopenharmony_ci	struct its_node *its = its_dev->its;
36248c2ecf20Sopenharmony_ci	int i;
36258c2ecf20Sopenharmony_ci
36268c2ecf20Sopenharmony_ci	bitmap_release_region(its_dev->event_map.lpi_map,
36278c2ecf20Sopenharmony_ci			      its_get_event_id(irq_domain_get_irq_data(domain, virq)),
36288c2ecf20Sopenharmony_ci			      get_count_order(nr_irqs));
36298c2ecf20Sopenharmony_ci
36308c2ecf20Sopenharmony_ci	for (i = 0; i < nr_irqs; i++) {
36318c2ecf20Sopenharmony_ci		struct irq_data *data = irq_domain_get_irq_data(domain,
36328c2ecf20Sopenharmony_ci								virq + i);
36338c2ecf20Sopenharmony_ci		/* Nuke the entry in the domain */
36348c2ecf20Sopenharmony_ci		irq_domain_reset_irq_data(data);
36358c2ecf20Sopenharmony_ci	}
36368c2ecf20Sopenharmony_ci
36378c2ecf20Sopenharmony_ci	mutex_lock(&its->dev_alloc_lock);
36388c2ecf20Sopenharmony_ci
36398c2ecf20Sopenharmony_ci	/*
36408c2ecf20Sopenharmony_ci	 * If all interrupts have been freed, start mopping the
36418c2ecf20Sopenharmony_ci	 * floor. This is conditioned on the device not being shared.
36428c2ecf20Sopenharmony_ci	 */
36438c2ecf20Sopenharmony_ci	if (!its_dev->shared &&
36448c2ecf20Sopenharmony_ci	    bitmap_empty(its_dev->event_map.lpi_map,
36458c2ecf20Sopenharmony_ci			 its_dev->event_map.nr_lpis)) {
36468c2ecf20Sopenharmony_ci		its_lpi_free(its_dev->event_map.lpi_map,
36478c2ecf20Sopenharmony_ci			     its_dev->event_map.lpi_base,
36488c2ecf20Sopenharmony_ci			     its_dev->event_map.nr_lpis);
36498c2ecf20Sopenharmony_ci
36508c2ecf20Sopenharmony_ci		/* Unmap device/itt */
36518c2ecf20Sopenharmony_ci		its_send_mapd(its_dev, 0);
36528c2ecf20Sopenharmony_ci		its_free_device(its_dev);
36538c2ecf20Sopenharmony_ci	}
36548c2ecf20Sopenharmony_ci
36558c2ecf20Sopenharmony_ci	mutex_unlock(&its->dev_alloc_lock);
36568c2ecf20Sopenharmony_ci
36578c2ecf20Sopenharmony_ci	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
36588c2ecf20Sopenharmony_ci}
36598c2ecf20Sopenharmony_ci
36608c2ecf20Sopenharmony_cistatic const struct irq_domain_ops its_domain_ops = {
36618c2ecf20Sopenharmony_ci	.alloc			= its_irq_domain_alloc,
36628c2ecf20Sopenharmony_ci	.free			= its_irq_domain_free,
36638c2ecf20Sopenharmony_ci	.activate		= its_irq_domain_activate,
36648c2ecf20Sopenharmony_ci	.deactivate		= its_irq_domain_deactivate,
36658c2ecf20Sopenharmony_ci};
36668c2ecf20Sopenharmony_ci
36678c2ecf20Sopenharmony_ci/*
36688c2ecf20Sopenharmony_ci * This is insane.
36698c2ecf20Sopenharmony_ci *
36708c2ecf20Sopenharmony_ci * If a GICv4.0 doesn't implement Direct LPIs (which is extremely
36718c2ecf20Sopenharmony_ci * likely), the only way to perform an invalidate is to use a fake
36728c2ecf20Sopenharmony_ci * device to issue an INV command, implying that the LPI has first
36738c2ecf20Sopenharmony_ci * been mapped to some event on that device. Since this is not exactly
36748c2ecf20Sopenharmony_ci * cheap, we try to keep that mapping around as long as possible, and
36758c2ecf20Sopenharmony_ci * only issue an UNMAP if we're short on available slots.
36768c2ecf20Sopenharmony_ci *
36778c2ecf20Sopenharmony_ci * Broken by design(tm).
36788c2ecf20Sopenharmony_ci *
36798c2ecf20Sopenharmony_ci * GICv4.1, on the other hand, mandates that we're able to invalidate
36808c2ecf20Sopenharmony_ci * by writing to a MMIO register. It doesn't implement the whole of
36818c2ecf20Sopenharmony_ci * DirectLPI, but that's good enough. And most of the time, we don't
36828c2ecf20Sopenharmony_ci * even have to invalidate anything, as the redistributor can be told
36838c2ecf20Sopenharmony_ci * whether to generate a doorbell or not (we thus leave it enabled,
36848c2ecf20Sopenharmony_ci * always).
36858c2ecf20Sopenharmony_ci */
36868c2ecf20Sopenharmony_cistatic void its_vpe_db_proxy_unmap_locked(struct its_vpe *vpe)
36878c2ecf20Sopenharmony_ci{
36888c2ecf20Sopenharmony_ci	/* GICv4.1 doesn't use a proxy, so nothing to do here */
36898c2ecf20Sopenharmony_ci	if (gic_rdists->has_rvpeid)
36908c2ecf20Sopenharmony_ci		return;
36918c2ecf20Sopenharmony_ci
36928c2ecf20Sopenharmony_ci	/* Already unmapped? */
36938c2ecf20Sopenharmony_ci	if (vpe->vpe_proxy_event == -1)
36948c2ecf20Sopenharmony_ci		return;
36958c2ecf20Sopenharmony_ci
36968c2ecf20Sopenharmony_ci	its_send_discard(vpe_proxy.dev, vpe->vpe_proxy_event);
36978c2ecf20Sopenharmony_ci	vpe_proxy.vpes[vpe->vpe_proxy_event] = NULL;
36988c2ecf20Sopenharmony_ci
36998c2ecf20Sopenharmony_ci	/*
37008c2ecf20Sopenharmony_ci	 * We don't track empty slots at all, so let's move the
37018c2ecf20Sopenharmony_ci	 * next_victim pointer if we can quickly reuse that slot
37028c2ecf20Sopenharmony_ci	 * instead of nuking an existing entry. Not clear that this is
37038c2ecf20Sopenharmony_ci	 * always a win though, and this might just generate a ripple
37048c2ecf20Sopenharmony_ci	 * effect... Let's just hope VPEs don't migrate too often.
37058c2ecf20Sopenharmony_ci	 */
37068c2ecf20Sopenharmony_ci	if (vpe_proxy.vpes[vpe_proxy.next_victim])
37078c2ecf20Sopenharmony_ci		vpe_proxy.next_victim = vpe->vpe_proxy_event;
37088c2ecf20Sopenharmony_ci
37098c2ecf20Sopenharmony_ci	vpe->vpe_proxy_event = -1;
37108c2ecf20Sopenharmony_ci}
37118c2ecf20Sopenharmony_ci
37128c2ecf20Sopenharmony_cistatic void its_vpe_db_proxy_unmap(struct its_vpe *vpe)
37138c2ecf20Sopenharmony_ci{
37148c2ecf20Sopenharmony_ci	/* GICv4.1 doesn't use a proxy, so nothing to do here */
37158c2ecf20Sopenharmony_ci	if (gic_rdists->has_rvpeid)
37168c2ecf20Sopenharmony_ci		return;
37178c2ecf20Sopenharmony_ci
37188c2ecf20Sopenharmony_ci	if (!gic_rdists->has_direct_lpi) {
37198c2ecf20Sopenharmony_ci		unsigned long flags;
37208c2ecf20Sopenharmony_ci
37218c2ecf20Sopenharmony_ci		raw_spin_lock_irqsave(&vpe_proxy.lock, flags);
37228c2ecf20Sopenharmony_ci		its_vpe_db_proxy_unmap_locked(vpe);
37238c2ecf20Sopenharmony_ci		raw_spin_unlock_irqrestore(&vpe_proxy.lock, flags);
37248c2ecf20Sopenharmony_ci	}
37258c2ecf20Sopenharmony_ci}
37268c2ecf20Sopenharmony_ci
37278c2ecf20Sopenharmony_cistatic void its_vpe_db_proxy_map_locked(struct its_vpe *vpe)
37288c2ecf20Sopenharmony_ci{
37298c2ecf20Sopenharmony_ci	/* GICv4.1 doesn't use a proxy, so nothing to do here */
37308c2ecf20Sopenharmony_ci	if (gic_rdists->has_rvpeid)
37318c2ecf20Sopenharmony_ci		return;
37328c2ecf20Sopenharmony_ci
37338c2ecf20Sopenharmony_ci	/* Already mapped? */
37348c2ecf20Sopenharmony_ci	if (vpe->vpe_proxy_event != -1)
37358c2ecf20Sopenharmony_ci		return;
37368c2ecf20Sopenharmony_ci
37378c2ecf20Sopenharmony_ci	/* This slot was already allocated. Kick the other VPE out. */
37388c2ecf20Sopenharmony_ci	if (vpe_proxy.vpes[vpe_proxy.next_victim])
37398c2ecf20Sopenharmony_ci		its_vpe_db_proxy_unmap_locked(vpe_proxy.vpes[vpe_proxy.next_victim]);
37408c2ecf20Sopenharmony_ci
37418c2ecf20Sopenharmony_ci	/* Map the new VPE instead */
37428c2ecf20Sopenharmony_ci	vpe_proxy.vpes[vpe_proxy.next_victim] = vpe;
37438c2ecf20Sopenharmony_ci	vpe->vpe_proxy_event = vpe_proxy.next_victim;
37448c2ecf20Sopenharmony_ci	vpe_proxy.next_victim = (vpe_proxy.next_victim + 1) % vpe_proxy.dev->nr_ites;
37458c2ecf20Sopenharmony_ci
37468c2ecf20Sopenharmony_ci	vpe_proxy.dev->event_map.col_map[vpe->vpe_proxy_event] = vpe->col_idx;
37478c2ecf20Sopenharmony_ci	its_send_mapti(vpe_proxy.dev, vpe->vpe_db_lpi, vpe->vpe_proxy_event);
37488c2ecf20Sopenharmony_ci}
37498c2ecf20Sopenharmony_ci
37508c2ecf20Sopenharmony_cistatic void its_vpe_db_proxy_move(struct its_vpe *vpe, int from, int to)
37518c2ecf20Sopenharmony_ci{
37528c2ecf20Sopenharmony_ci	unsigned long flags;
37538c2ecf20Sopenharmony_ci	struct its_collection *target_col;
37548c2ecf20Sopenharmony_ci
37558c2ecf20Sopenharmony_ci	/* GICv4.1 doesn't use a proxy, so nothing to do here */
37568c2ecf20Sopenharmony_ci	if (gic_rdists->has_rvpeid)
37578c2ecf20Sopenharmony_ci		return;
37588c2ecf20Sopenharmony_ci
37598c2ecf20Sopenharmony_ci	if (gic_rdists->has_direct_lpi) {
37608c2ecf20Sopenharmony_ci		void __iomem *rdbase;
37618c2ecf20Sopenharmony_ci
37628c2ecf20Sopenharmony_ci		rdbase = per_cpu_ptr(gic_rdists->rdist, from)->rd_base;
37638c2ecf20Sopenharmony_ci		gic_write_lpir(vpe->vpe_db_lpi, rdbase + GICR_CLRLPIR);
37648c2ecf20Sopenharmony_ci		wait_for_syncr(rdbase);
37658c2ecf20Sopenharmony_ci
37668c2ecf20Sopenharmony_ci		return;
37678c2ecf20Sopenharmony_ci	}
37688c2ecf20Sopenharmony_ci
37698c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&vpe_proxy.lock, flags);
37708c2ecf20Sopenharmony_ci
37718c2ecf20Sopenharmony_ci	its_vpe_db_proxy_map_locked(vpe);
37728c2ecf20Sopenharmony_ci
37738c2ecf20Sopenharmony_ci	target_col = &vpe_proxy.dev->its->collections[to];
37748c2ecf20Sopenharmony_ci	its_send_movi(vpe_proxy.dev, target_col, vpe->vpe_proxy_event);
37758c2ecf20Sopenharmony_ci	vpe_proxy.dev->event_map.col_map[vpe->vpe_proxy_event] = to;
37768c2ecf20Sopenharmony_ci
37778c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&vpe_proxy.lock, flags);
37788c2ecf20Sopenharmony_ci}
37798c2ecf20Sopenharmony_ci
37808c2ecf20Sopenharmony_cistatic int its_vpe_set_affinity(struct irq_data *d,
37818c2ecf20Sopenharmony_ci				const struct cpumask *mask_val,
37828c2ecf20Sopenharmony_ci				bool force)
37838c2ecf20Sopenharmony_ci{
37848c2ecf20Sopenharmony_ci	struct its_vpe *vpe = irq_data_get_irq_chip_data(d);
37858c2ecf20Sopenharmony_ci	struct cpumask common, *table_mask;
37868c2ecf20Sopenharmony_ci	unsigned long flags;
37878c2ecf20Sopenharmony_ci	int from, cpu;
37888c2ecf20Sopenharmony_ci
37898c2ecf20Sopenharmony_ci	/*
37908c2ecf20Sopenharmony_ci	 * Changing affinity is mega expensive, so let's be as lazy as
37918c2ecf20Sopenharmony_ci	 * we can and only do it if we really have to. Also, if mapped
37928c2ecf20Sopenharmony_ci	 * into the proxy device, we need to move the doorbell
37938c2ecf20Sopenharmony_ci	 * interrupt to its new location.
37948c2ecf20Sopenharmony_ci	 *
37958c2ecf20Sopenharmony_ci	 * Another thing is that changing the affinity of a vPE affects
37968c2ecf20Sopenharmony_ci	 * *other interrupts* such as all the vLPIs that are routed to
37978c2ecf20Sopenharmony_ci	 * this vPE. This means that the irq_desc lock is not enough to
37988c2ecf20Sopenharmony_ci	 * protect us, and that we must ensure nobody samples vpe->col_idx
37998c2ecf20Sopenharmony_ci	 * during the update, hence the lock below which must also be
38008c2ecf20Sopenharmony_ci	 * taken on any vLPI handling path that evaluates vpe->col_idx.
38018c2ecf20Sopenharmony_ci	 */
38028c2ecf20Sopenharmony_ci	from = vpe_to_cpuid_lock(vpe, &flags);
38038c2ecf20Sopenharmony_ci	table_mask = gic_data_rdist_cpu(from)->vpe_table_mask;
38048c2ecf20Sopenharmony_ci
38058c2ecf20Sopenharmony_ci	/*
38068c2ecf20Sopenharmony_ci	 * If we are offered another CPU in the same GICv4.1 ITS
38078c2ecf20Sopenharmony_ci	 * affinity, pick this one. Otherwise, any CPU will do.
38088c2ecf20Sopenharmony_ci	 */
38098c2ecf20Sopenharmony_ci	if (table_mask && cpumask_and(&common, mask_val, table_mask))
38108c2ecf20Sopenharmony_ci		cpu = cpumask_test_cpu(from, &common) ? from : cpumask_first(&common);
38118c2ecf20Sopenharmony_ci	else
38128c2ecf20Sopenharmony_ci		cpu = cpumask_first(mask_val);
38138c2ecf20Sopenharmony_ci
38148c2ecf20Sopenharmony_ci	if (from == cpu)
38158c2ecf20Sopenharmony_ci		goto out;
38168c2ecf20Sopenharmony_ci
38178c2ecf20Sopenharmony_ci	vpe->col_idx = cpu;
38188c2ecf20Sopenharmony_ci
38198c2ecf20Sopenharmony_ci	its_send_vmovp(vpe);
38208c2ecf20Sopenharmony_ci	its_vpe_db_proxy_move(vpe, from, cpu);
38218c2ecf20Sopenharmony_ci
38228c2ecf20Sopenharmony_ciout:
38238c2ecf20Sopenharmony_ci	irq_data_update_effective_affinity(d, cpumask_of(cpu));
38248c2ecf20Sopenharmony_ci	vpe_to_cpuid_unlock(vpe, flags);
38258c2ecf20Sopenharmony_ci
38268c2ecf20Sopenharmony_ci	return IRQ_SET_MASK_OK_DONE;
38278c2ecf20Sopenharmony_ci}
38288c2ecf20Sopenharmony_ci
38298c2ecf20Sopenharmony_cistatic void its_wait_vpt_parse_complete(void)
38308c2ecf20Sopenharmony_ci{
38318c2ecf20Sopenharmony_ci	void __iomem *vlpi_base = gic_data_rdist_vlpi_base();
38328c2ecf20Sopenharmony_ci	u64 val;
38338c2ecf20Sopenharmony_ci
38348c2ecf20Sopenharmony_ci	if (!gic_rdists->has_vpend_valid_dirty)
38358c2ecf20Sopenharmony_ci		return;
38368c2ecf20Sopenharmony_ci
38378c2ecf20Sopenharmony_ci	WARN_ON_ONCE(readq_relaxed_poll_timeout_atomic(vlpi_base + GICR_VPENDBASER,
38388c2ecf20Sopenharmony_ci						       val,
38398c2ecf20Sopenharmony_ci						       !(val & GICR_VPENDBASER_Dirty),
38408c2ecf20Sopenharmony_ci						       10, 500));
38418c2ecf20Sopenharmony_ci}
38428c2ecf20Sopenharmony_ci
38438c2ecf20Sopenharmony_cistatic void its_vpe_schedule(struct its_vpe *vpe)
38448c2ecf20Sopenharmony_ci{
38458c2ecf20Sopenharmony_ci	void __iomem *vlpi_base = gic_data_rdist_vlpi_base();
38468c2ecf20Sopenharmony_ci	u64 val;
38478c2ecf20Sopenharmony_ci
38488c2ecf20Sopenharmony_ci	/* Schedule the VPE */
38498c2ecf20Sopenharmony_ci	val  = virt_to_phys(page_address(vpe->its_vm->vprop_page)) &
38508c2ecf20Sopenharmony_ci		GENMASK_ULL(51, 12);
38518c2ecf20Sopenharmony_ci	val |= (LPI_NRBITS - 1) & GICR_VPROPBASER_IDBITS_MASK;
38528c2ecf20Sopenharmony_ci	val |= GICR_VPROPBASER_RaWb;
38538c2ecf20Sopenharmony_ci	val |= GICR_VPROPBASER_InnerShareable;
38548c2ecf20Sopenharmony_ci	gicr_write_vpropbaser(val, vlpi_base + GICR_VPROPBASER);
38558c2ecf20Sopenharmony_ci
38568c2ecf20Sopenharmony_ci	val  = virt_to_phys(page_address(vpe->vpt_page)) &
38578c2ecf20Sopenharmony_ci		GENMASK_ULL(51, 16);
38588c2ecf20Sopenharmony_ci	val |= GICR_VPENDBASER_RaWaWb;
38598c2ecf20Sopenharmony_ci	val |= GICR_VPENDBASER_InnerShareable;
38608c2ecf20Sopenharmony_ci	/*
38618c2ecf20Sopenharmony_ci	 * There is no good way of finding out if the pending table is
38628c2ecf20Sopenharmony_ci	 * empty as we can race against the doorbell interrupt very
38638c2ecf20Sopenharmony_ci	 * easily. So in the end, vpe->pending_last is only an
38648c2ecf20Sopenharmony_ci	 * indication that the vcpu has something pending, not one
38658c2ecf20Sopenharmony_ci	 * that the pending table is empty. A good implementation
38668c2ecf20Sopenharmony_ci	 * would be able to read its coarse map pretty quickly anyway,
38678c2ecf20Sopenharmony_ci	 * making this a tolerable issue.
38688c2ecf20Sopenharmony_ci	 */
38698c2ecf20Sopenharmony_ci	val |= GICR_VPENDBASER_PendingLast;
38708c2ecf20Sopenharmony_ci	val |= vpe->idai ? GICR_VPENDBASER_IDAI : 0;
38718c2ecf20Sopenharmony_ci	val |= GICR_VPENDBASER_Valid;
38728c2ecf20Sopenharmony_ci	gicr_write_vpendbaser(val, vlpi_base + GICR_VPENDBASER);
38738c2ecf20Sopenharmony_ci
38748c2ecf20Sopenharmony_ci	its_wait_vpt_parse_complete();
38758c2ecf20Sopenharmony_ci}
38768c2ecf20Sopenharmony_ci
38778c2ecf20Sopenharmony_cistatic void its_vpe_deschedule(struct its_vpe *vpe)
38788c2ecf20Sopenharmony_ci{
38798c2ecf20Sopenharmony_ci	void __iomem *vlpi_base = gic_data_rdist_vlpi_base();
38808c2ecf20Sopenharmony_ci	u64 val;
38818c2ecf20Sopenharmony_ci
38828c2ecf20Sopenharmony_ci	val = its_clear_vpend_valid(vlpi_base, 0, 0);
38838c2ecf20Sopenharmony_ci
38848c2ecf20Sopenharmony_ci	vpe->idai = !!(val & GICR_VPENDBASER_IDAI);
38858c2ecf20Sopenharmony_ci	vpe->pending_last = !!(val & GICR_VPENDBASER_PendingLast);
38868c2ecf20Sopenharmony_ci}
38878c2ecf20Sopenharmony_ci
38888c2ecf20Sopenharmony_cistatic void its_vpe_invall(struct its_vpe *vpe)
38898c2ecf20Sopenharmony_ci{
38908c2ecf20Sopenharmony_ci	struct its_node *its;
38918c2ecf20Sopenharmony_ci
38928c2ecf20Sopenharmony_ci	list_for_each_entry(its, &its_nodes, entry) {
38938c2ecf20Sopenharmony_ci		if (!is_v4(its))
38948c2ecf20Sopenharmony_ci			continue;
38958c2ecf20Sopenharmony_ci
38968c2ecf20Sopenharmony_ci		if (its_list_map && !vpe->its_vm->vlpi_count[its->list_nr])
38978c2ecf20Sopenharmony_ci			continue;
38988c2ecf20Sopenharmony_ci
38998c2ecf20Sopenharmony_ci		/*
39008c2ecf20Sopenharmony_ci		 * Sending a VINVALL to a single ITS is enough, as all
39018c2ecf20Sopenharmony_ci		 * we need is to reach the redistributors.
39028c2ecf20Sopenharmony_ci		 */
39038c2ecf20Sopenharmony_ci		its_send_vinvall(its, vpe);
39048c2ecf20Sopenharmony_ci		return;
39058c2ecf20Sopenharmony_ci	}
39068c2ecf20Sopenharmony_ci}
39078c2ecf20Sopenharmony_ci
39088c2ecf20Sopenharmony_cistatic int its_vpe_set_vcpu_affinity(struct irq_data *d, void *vcpu_info)
39098c2ecf20Sopenharmony_ci{
39108c2ecf20Sopenharmony_ci	struct its_vpe *vpe = irq_data_get_irq_chip_data(d);
39118c2ecf20Sopenharmony_ci	struct its_cmd_info *info = vcpu_info;
39128c2ecf20Sopenharmony_ci
39138c2ecf20Sopenharmony_ci	switch (info->cmd_type) {
39148c2ecf20Sopenharmony_ci	case SCHEDULE_VPE:
39158c2ecf20Sopenharmony_ci		its_vpe_schedule(vpe);
39168c2ecf20Sopenharmony_ci		return 0;
39178c2ecf20Sopenharmony_ci
39188c2ecf20Sopenharmony_ci	case DESCHEDULE_VPE:
39198c2ecf20Sopenharmony_ci		its_vpe_deschedule(vpe);
39208c2ecf20Sopenharmony_ci		return 0;
39218c2ecf20Sopenharmony_ci
39228c2ecf20Sopenharmony_ci	case INVALL_VPE:
39238c2ecf20Sopenharmony_ci		its_vpe_invall(vpe);
39248c2ecf20Sopenharmony_ci		return 0;
39258c2ecf20Sopenharmony_ci
39268c2ecf20Sopenharmony_ci	default:
39278c2ecf20Sopenharmony_ci		return -EINVAL;
39288c2ecf20Sopenharmony_ci	}
39298c2ecf20Sopenharmony_ci}
39308c2ecf20Sopenharmony_ci
39318c2ecf20Sopenharmony_cistatic void its_vpe_send_cmd(struct its_vpe *vpe,
39328c2ecf20Sopenharmony_ci			     void (*cmd)(struct its_device *, u32))
39338c2ecf20Sopenharmony_ci{
39348c2ecf20Sopenharmony_ci	unsigned long flags;
39358c2ecf20Sopenharmony_ci
39368c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&vpe_proxy.lock, flags);
39378c2ecf20Sopenharmony_ci
39388c2ecf20Sopenharmony_ci	its_vpe_db_proxy_map_locked(vpe);
39398c2ecf20Sopenharmony_ci	cmd(vpe_proxy.dev, vpe->vpe_proxy_event);
39408c2ecf20Sopenharmony_ci
39418c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&vpe_proxy.lock, flags);
39428c2ecf20Sopenharmony_ci}
39438c2ecf20Sopenharmony_ci
39448c2ecf20Sopenharmony_cistatic void its_vpe_send_inv(struct irq_data *d)
39458c2ecf20Sopenharmony_ci{
39468c2ecf20Sopenharmony_ci	struct its_vpe *vpe = irq_data_get_irq_chip_data(d);
39478c2ecf20Sopenharmony_ci
39488c2ecf20Sopenharmony_ci	if (gic_rdists->has_direct_lpi)
39498c2ecf20Sopenharmony_ci		__direct_lpi_inv(d, d->parent_data->hwirq);
39508c2ecf20Sopenharmony_ci	else
39518c2ecf20Sopenharmony_ci		its_vpe_send_cmd(vpe, its_send_inv);
39528c2ecf20Sopenharmony_ci}
39538c2ecf20Sopenharmony_ci
39548c2ecf20Sopenharmony_cistatic void its_vpe_mask_irq(struct irq_data *d)
39558c2ecf20Sopenharmony_ci{
39568c2ecf20Sopenharmony_ci	/*
39578c2ecf20Sopenharmony_ci	 * We need to unmask the LPI, which is described by the parent
39588c2ecf20Sopenharmony_ci	 * irq_data. Instead of calling into the parent (which won't
39598c2ecf20Sopenharmony_ci	 * exactly do the right thing, let's simply use the
39608c2ecf20Sopenharmony_ci	 * parent_data pointer. Yes, I'm naughty.
39618c2ecf20Sopenharmony_ci	 */
39628c2ecf20Sopenharmony_ci	lpi_write_config(d->parent_data, LPI_PROP_ENABLED, 0);
39638c2ecf20Sopenharmony_ci	its_vpe_send_inv(d);
39648c2ecf20Sopenharmony_ci}
39658c2ecf20Sopenharmony_ci
39668c2ecf20Sopenharmony_cistatic void its_vpe_unmask_irq(struct irq_data *d)
39678c2ecf20Sopenharmony_ci{
39688c2ecf20Sopenharmony_ci	/* Same hack as above... */
39698c2ecf20Sopenharmony_ci	lpi_write_config(d->parent_data, 0, LPI_PROP_ENABLED);
39708c2ecf20Sopenharmony_ci	its_vpe_send_inv(d);
39718c2ecf20Sopenharmony_ci}
39728c2ecf20Sopenharmony_ci
39738c2ecf20Sopenharmony_cistatic int its_vpe_set_irqchip_state(struct irq_data *d,
39748c2ecf20Sopenharmony_ci				     enum irqchip_irq_state which,
39758c2ecf20Sopenharmony_ci				     bool state)
39768c2ecf20Sopenharmony_ci{
39778c2ecf20Sopenharmony_ci	struct its_vpe *vpe = irq_data_get_irq_chip_data(d);
39788c2ecf20Sopenharmony_ci
39798c2ecf20Sopenharmony_ci	if (which != IRQCHIP_STATE_PENDING)
39808c2ecf20Sopenharmony_ci		return -EINVAL;
39818c2ecf20Sopenharmony_ci
39828c2ecf20Sopenharmony_ci	if (gic_rdists->has_direct_lpi) {
39838c2ecf20Sopenharmony_ci		void __iomem *rdbase;
39848c2ecf20Sopenharmony_ci
39858c2ecf20Sopenharmony_ci		rdbase = per_cpu_ptr(gic_rdists->rdist, vpe->col_idx)->rd_base;
39868c2ecf20Sopenharmony_ci		if (state) {
39878c2ecf20Sopenharmony_ci			gic_write_lpir(vpe->vpe_db_lpi, rdbase + GICR_SETLPIR);
39888c2ecf20Sopenharmony_ci		} else {
39898c2ecf20Sopenharmony_ci			gic_write_lpir(vpe->vpe_db_lpi, rdbase + GICR_CLRLPIR);
39908c2ecf20Sopenharmony_ci			wait_for_syncr(rdbase);
39918c2ecf20Sopenharmony_ci		}
39928c2ecf20Sopenharmony_ci	} else {
39938c2ecf20Sopenharmony_ci		if (state)
39948c2ecf20Sopenharmony_ci			its_vpe_send_cmd(vpe, its_send_int);
39958c2ecf20Sopenharmony_ci		else
39968c2ecf20Sopenharmony_ci			its_vpe_send_cmd(vpe, its_send_clear);
39978c2ecf20Sopenharmony_ci	}
39988c2ecf20Sopenharmony_ci
39998c2ecf20Sopenharmony_ci	return 0;
40008c2ecf20Sopenharmony_ci}
40018c2ecf20Sopenharmony_ci
40028c2ecf20Sopenharmony_cistatic int its_vpe_retrigger(struct irq_data *d)
40038c2ecf20Sopenharmony_ci{
40048c2ecf20Sopenharmony_ci	return !its_vpe_set_irqchip_state(d, IRQCHIP_STATE_PENDING, true);
40058c2ecf20Sopenharmony_ci}
40068c2ecf20Sopenharmony_ci
40078c2ecf20Sopenharmony_cistatic struct irq_chip its_vpe_irq_chip = {
40088c2ecf20Sopenharmony_ci	.name			= "GICv4-vpe",
40098c2ecf20Sopenharmony_ci	.irq_mask		= its_vpe_mask_irq,
40108c2ecf20Sopenharmony_ci	.irq_unmask		= its_vpe_unmask_irq,
40118c2ecf20Sopenharmony_ci	.irq_eoi		= irq_chip_eoi_parent,
40128c2ecf20Sopenharmony_ci	.irq_set_affinity	= its_vpe_set_affinity,
40138c2ecf20Sopenharmony_ci	.irq_retrigger		= its_vpe_retrigger,
40148c2ecf20Sopenharmony_ci	.irq_set_irqchip_state	= its_vpe_set_irqchip_state,
40158c2ecf20Sopenharmony_ci	.irq_set_vcpu_affinity	= its_vpe_set_vcpu_affinity,
40168c2ecf20Sopenharmony_ci};
40178c2ecf20Sopenharmony_ci
40188c2ecf20Sopenharmony_cistatic struct its_node *find_4_1_its(void)
40198c2ecf20Sopenharmony_ci{
40208c2ecf20Sopenharmony_ci	static struct its_node *its = NULL;
40218c2ecf20Sopenharmony_ci
40228c2ecf20Sopenharmony_ci	if (!its) {
40238c2ecf20Sopenharmony_ci		list_for_each_entry(its, &its_nodes, entry) {
40248c2ecf20Sopenharmony_ci			if (is_v4_1(its))
40258c2ecf20Sopenharmony_ci				return its;
40268c2ecf20Sopenharmony_ci		}
40278c2ecf20Sopenharmony_ci
40288c2ecf20Sopenharmony_ci		/* Oops? */
40298c2ecf20Sopenharmony_ci		its = NULL;
40308c2ecf20Sopenharmony_ci	}
40318c2ecf20Sopenharmony_ci
40328c2ecf20Sopenharmony_ci	return its;
40338c2ecf20Sopenharmony_ci}
40348c2ecf20Sopenharmony_ci
40358c2ecf20Sopenharmony_cistatic void its_vpe_4_1_send_inv(struct irq_data *d)
40368c2ecf20Sopenharmony_ci{
40378c2ecf20Sopenharmony_ci	struct its_vpe *vpe = irq_data_get_irq_chip_data(d);
40388c2ecf20Sopenharmony_ci	struct its_node *its;
40398c2ecf20Sopenharmony_ci
40408c2ecf20Sopenharmony_ci	/*
40418c2ecf20Sopenharmony_ci	 * GICv4.1 wants doorbells to be invalidated using the
40428c2ecf20Sopenharmony_ci	 * INVDB command in order to be broadcast to all RDs. Send
40438c2ecf20Sopenharmony_ci	 * it to the first valid ITS, and let the HW do its magic.
40448c2ecf20Sopenharmony_ci	 */
40458c2ecf20Sopenharmony_ci	its = find_4_1_its();
40468c2ecf20Sopenharmony_ci	if (its)
40478c2ecf20Sopenharmony_ci		its_send_invdb(its, vpe);
40488c2ecf20Sopenharmony_ci}
40498c2ecf20Sopenharmony_ci
40508c2ecf20Sopenharmony_cistatic void its_vpe_4_1_mask_irq(struct irq_data *d)
40518c2ecf20Sopenharmony_ci{
40528c2ecf20Sopenharmony_ci	lpi_write_config(d->parent_data, LPI_PROP_ENABLED, 0);
40538c2ecf20Sopenharmony_ci	its_vpe_4_1_send_inv(d);
40548c2ecf20Sopenharmony_ci}
40558c2ecf20Sopenharmony_ci
40568c2ecf20Sopenharmony_cistatic void its_vpe_4_1_unmask_irq(struct irq_data *d)
40578c2ecf20Sopenharmony_ci{
40588c2ecf20Sopenharmony_ci	lpi_write_config(d->parent_data, 0, LPI_PROP_ENABLED);
40598c2ecf20Sopenharmony_ci	its_vpe_4_1_send_inv(d);
40608c2ecf20Sopenharmony_ci}
40618c2ecf20Sopenharmony_ci
40628c2ecf20Sopenharmony_cistatic void its_vpe_4_1_schedule(struct its_vpe *vpe,
40638c2ecf20Sopenharmony_ci				 struct its_cmd_info *info)
40648c2ecf20Sopenharmony_ci{
40658c2ecf20Sopenharmony_ci	void __iomem *vlpi_base = gic_data_rdist_vlpi_base();
40668c2ecf20Sopenharmony_ci	u64 val = 0;
40678c2ecf20Sopenharmony_ci
40688c2ecf20Sopenharmony_ci	/* Schedule the VPE */
40698c2ecf20Sopenharmony_ci	val |= GICR_VPENDBASER_Valid;
40708c2ecf20Sopenharmony_ci	val |= info->g0en ? GICR_VPENDBASER_4_1_VGRP0EN : 0;
40718c2ecf20Sopenharmony_ci	val |= info->g1en ? GICR_VPENDBASER_4_1_VGRP1EN : 0;
40728c2ecf20Sopenharmony_ci	val |= FIELD_PREP(GICR_VPENDBASER_4_1_VPEID, vpe->vpe_id);
40738c2ecf20Sopenharmony_ci
40748c2ecf20Sopenharmony_ci	gicr_write_vpendbaser(val, vlpi_base + GICR_VPENDBASER);
40758c2ecf20Sopenharmony_ci
40768c2ecf20Sopenharmony_ci	its_wait_vpt_parse_complete();
40778c2ecf20Sopenharmony_ci}
40788c2ecf20Sopenharmony_ci
40798c2ecf20Sopenharmony_cistatic void its_vpe_4_1_deschedule(struct its_vpe *vpe,
40808c2ecf20Sopenharmony_ci				   struct its_cmd_info *info)
40818c2ecf20Sopenharmony_ci{
40828c2ecf20Sopenharmony_ci	void __iomem *vlpi_base = gic_data_rdist_vlpi_base();
40838c2ecf20Sopenharmony_ci	u64 val;
40848c2ecf20Sopenharmony_ci
40858c2ecf20Sopenharmony_ci	if (info->req_db) {
40868c2ecf20Sopenharmony_ci		unsigned long flags;
40878c2ecf20Sopenharmony_ci
40888c2ecf20Sopenharmony_ci		/*
40898c2ecf20Sopenharmony_ci		 * vPE is going to block: make the vPE non-resident with
40908c2ecf20Sopenharmony_ci		 * PendingLast clear and DB set. The GIC guarantees that if
40918c2ecf20Sopenharmony_ci		 * we read-back PendingLast clear, then a doorbell will be
40928c2ecf20Sopenharmony_ci		 * delivered when an interrupt comes.
40938c2ecf20Sopenharmony_ci		 *
40948c2ecf20Sopenharmony_ci		 * Note the locking to deal with the concurrent update of
40958c2ecf20Sopenharmony_ci		 * pending_last from the doorbell interrupt handler that can
40968c2ecf20Sopenharmony_ci		 * run concurrently.
40978c2ecf20Sopenharmony_ci		 */
40988c2ecf20Sopenharmony_ci		raw_spin_lock_irqsave(&vpe->vpe_lock, flags);
40998c2ecf20Sopenharmony_ci		val = its_clear_vpend_valid(vlpi_base,
41008c2ecf20Sopenharmony_ci					    GICR_VPENDBASER_PendingLast,
41018c2ecf20Sopenharmony_ci					    GICR_VPENDBASER_4_1_DB);
41028c2ecf20Sopenharmony_ci		vpe->pending_last = !!(val & GICR_VPENDBASER_PendingLast);
41038c2ecf20Sopenharmony_ci		raw_spin_unlock_irqrestore(&vpe->vpe_lock, flags);
41048c2ecf20Sopenharmony_ci	} else {
41058c2ecf20Sopenharmony_ci		/*
41068c2ecf20Sopenharmony_ci		 * We're not blocking, so just make the vPE non-resident
41078c2ecf20Sopenharmony_ci		 * with PendingLast set, indicating that we'll be back.
41088c2ecf20Sopenharmony_ci		 */
41098c2ecf20Sopenharmony_ci		val = its_clear_vpend_valid(vlpi_base,
41108c2ecf20Sopenharmony_ci					    0,
41118c2ecf20Sopenharmony_ci					    GICR_VPENDBASER_PendingLast);
41128c2ecf20Sopenharmony_ci		vpe->pending_last = true;
41138c2ecf20Sopenharmony_ci	}
41148c2ecf20Sopenharmony_ci}
41158c2ecf20Sopenharmony_ci
41168c2ecf20Sopenharmony_cistatic void its_vpe_4_1_invall(struct its_vpe *vpe)
41178c2ecf20Sopenharmony_ci{
41188c2ecf20Sopenharmony_ci	void __iomem *rdbase;
41198c2ecf20Sopenharmony_ci	unsigned long flags;
41208c2ecf20Sopenharmony_ci	u64 val;
41218c2ecf20Sopenharmony_ci	int cpu;
41228c2ecf20Sopenharmony_ci
41238c2ecf20Sopenharmony_ci	val  = GICR_INVALLR_V;
41248c2ecf20Sopenharmony_ci	val |= FIELD_PREP(GICR_INVALLR_VPEID, vpe->vpe_id);
41258c2ecf20Sopenharmony_ci
41268c2ecf20Sopenharmony_ci	/* Target the redistributor this vPE is currently known on */
41278c2ecf20Sopenharmony_ci	cpu = vpe_to_cpuid_lock(vpe, &flags);
41288c2ecf20Sopenharmony_ci	raw_spin_lock(&gic_data_rdist_cpu(cpu)->rd_lock);
41298c2ecf20Sopenharmony_ci	rdbase = per_cpu_ptr(gic_rdists->rdist, cpu)->rd_base;
41308c2ecf20Sopenharmony_ci	gic_write_lpir(val, rdbase + GICR_INVALLR);
41318c2ecf20Sopenharmony_ci
41328c2ecf20Sopenharmony_ci	wait_for_syncr(rdbase);
41338c2ecf20Sopenharmony_ci	raw_spin_unlock(&gic_data_rdist_cpu(cpu)->rd_lock);
41348c2ecf20Sopenharmony_ci	vpe_to_cpuid_unlock(vpe, flags);
41358c2ecf20Sopenharmony_ci}
41368c2ecf20Sopenharmony_ci
41378c2ecf20Sopenharmony_cistatic int its_vpe_4_1_set_vcpu_affinity(struct irq_data *d, void *vcpu_info)
41388c2ecf20Sopenharmony_ci{
41398c2ecf20Sopenharmony_ci	struct its_vpe *vpe = irq_data_get_irq_chip_data(d);
41408c2ecf20Sopenharmony_ci	struct its_cmd_info *info = vcpu_info;
41418c2ecf20Sopenharmony_ci
41428c2ecf20Sopenharmony_ci	switch (info->cmd_type) {
41438c2ecf20Sopenharmony_ci	case SCHEDULE_VPE:
41448c2ecf20Sopenharmony_ci		its_vpe_4_1_schedule(vpe, info);
41458c2ecf20Sopenharmony_ci		return 0;
41468c2ecf20Sopenharmony_ci
41478c2ecf20Sopenharmony_ci	case DESCHEDULE_VPE:
41488c2ecf20Sopenharmony_ci		its_vpe_4_1_deschedule(vpe, info);
41498c2ecf20Sopenharmony_ci		return 0;
41508c2ecf20Sopenharmony_ci
41518c2ecf20Sopenharmony_ci	case INVALL_VPE:
41528c2ecf20Sopenharmony_ci		its_vpe_4_1_invall(vpe);
41538c2ecf20Sopenharmony_ci		return 0;
41548c2ecf20Sopenharmony_ci
41558c2ecf20Sopenharmony_ci	default:
41568c2ecf20Sopenharmony_ci		return -EINVAL;
41578c2ecf20Sopenharmony_ci	}
41588c2ecf20Sopenharmony_ci}
41598c2ecf20Sopenharmony_ci
41608c2ecf20Sopenharmony_cistatic struct irq_chip its_vpe_4_1_irq_chip = {
41618c2ecf20Sopenharmony_ci	.name			= "GICv4.1-vpe",
41628c2ecf20Sopenharmony_ci	.irq_mask		= its_vpe_4_1_mask_irq,
41638c2ecf20Sopenharmony_ci	.irq_unmask		= its_vpe_4_1_unmask_irq,
41648c2ecf20Sopenharmony_ci	.irq_eoi		= irq_chip_eoi_parent,
41658c2ecf20Sopenharmony_ci	.irq_set_affinity	= its_vpe_set_affinity,
41668c2ecf20Sopenharmony_ci	.irq_set_vcpu_affinity	= its_vpe_4_1_set_vcpu_affinity,
41678c2ecf20Sopenharmony_ci};
41688c2ecf20Sopenharmony_ci
41698c2ecf20Sopenharmony_cistatic void its_configure_sgi(struct irq_data *d, bool clear)
41708c2ecf20Sopenharmony_ci{
41718c2ecf20Sopenharmony_ci	struct its_vpe *vpe = irq_data_get_irq_chip_data(d);
41728c2ecf20Sopenharmony_ci	struct its_cmd_desc desc;
41738c2ecf20Sopenharmony_ci
41748c2ecf20Sopenharmony_ci	desc.its_vsgi_cmd.vpe = vpe;
41758c2ecf20Sopenharmony_ci	desc.its_vsgi_cmd.sgi = d->hwirq;
41768c2ecf20Sopenharmony_ci	desc.its_vsgi_cmd.priority = vpe->sgi_config[d->hwirq].priority;
41778c2ecf20Sopenharmony_ci	desc.its_vsgi_cmd.enable = vpe->sgi_config[d->hwirq].enabled;
41788c2ecf20Sopenharmony_ci	desc.its_vsgi_cmd.group = vpe->sgi_config[d->hwirq].group;
41798c2ecf20Sopenharmony_ci	desc.its_vsgi_cmd.clear = clear;
41808c2ecf20Sopenharmony_ci
41818c2ecf20Sopenharmony_ci	/*
41828c2ecf20Sopenharmony_ci	 * GICv4.1 allows us to send VSGI commands to any ITS as long as the
41838c2ecf20Sopenharmony_ci	 * destination VPE is mapped there. Since we map them eagerly at
41848c2ecf20Sopenharmony_ci	 * activation time, we're pretty sure the first GICv4.1 ITS will do.
41858c2ecf20Sopenharmony_ci	 */
41868c2ecf20Sopenharmony_ci	its_send_single_vcommand(find_4_1_its(), its_build_vsgi_cmd, &desc);
41878c2ecf20Sopenharmony_ci}
41888c2ecf20Sopenharmony_ci
41898c2ecf20Sopenharmony_cistatic void its_sgi_mask_irq(struct irq_data *d)
41908c2ecf20Sopenharmony_ci{
41918c2ecf20Sopenharmony_ci	struct its_vpe *vpe = irq_data_get_irq_chip_data(d);
41928c2ecf20Sopenharmony_ci
41938c2ecf20Sopenharmony_ci	vpe->sgi_config[d->hwirq].enabled = false;
41948c2ecf20Sopenharmony_ci	its_configure_sgi(d, false);
41958c2ecf20Sopenharmony_ci}
41968c2ecf20Sopenharmony_ci
41978c2ecf20Sopenharmony_cistatic void its_sgi_unmask_irq(struct irq_data *d)
41988c2ecf20Sopenharmony_ci{
41998c2ecf20Sopenharmony_ci	struct its_vpe *vpe = irq_data_get_irq_chip_data(d);
42008c2ecf20Sopenharmony_ci
42018c2ecf20Sopenharmony_ci	vpe->sgi_config[d->hwirq].enabled = true;
42028c2ecf20Sopenharmony_ci	its_configure_sgi(d, false);
42038c2ecf20Sopenharmony_ci}
42048c2ecf20Sopenharmony_ci
42058c2ecf20Sopenharmony_cistatic int its_sgi_set_affinity(struct irq_data *d,
42068c2ecf20Sopenharmony_ci				const struct cpumask *mask_val,
42078c2ecf20Sopenharmony_ci				bool force)
42088c2ecf20Sopenharmony_ci{
42098c2ecf20Sopenharmony_ci	/*
42108c2ecf20Sopenharmony_ci	 * There is no notion of affinity for virtual SGIs, at least
42118c2ecf20Sopenharmony_ci	 * not on the host (since they can only be targeting a vPE).
42128c2ecf20Sopenharmony_ci	 * Tell the kernel we've done whatever it asked for.
42138c2ecf20Sopenharmony_ci	 */
42148c2ecf20Sopenharmony_ci	irq_data_update_effective_affinity(d, mask_val);
42158c2ecf20Sopenharmony_ci	return IRQ_SET_MASK_OK;
42168c2ecf20Sopenharmony_ci}
42178c2ecf20Sopenharmony_ci
42188c2ecf20Sopenharmony_cistatic int its_sgi_set_irqchip_state(struct irq_data *d,
42198c2ecf20Sopenharmony_ci				     enum irqchip_irq_state which,
42208c2ecf20Sopenharmony_ci				     bool state)
42218c2ecf20Sopenharmony_ci{
42228c2ecf20Sopenharmony_ci	if (which != IRQCHIP_STATE_PENDING)
42238c2ecf20Sopenharmony_ci		return -EINVAL;
42248c2ecf20Sopenharmony_ci
42258c2ecf20Sopenharmony_ci	if (state) {
42268c2ecf20Sopenharmony_ci		struct its_vpe *vpe = irq_data_get_irq_chip_data(d);
42278c2ecf20Sopenharmony_ci		struct its_node *its = find_4_1_its();
42288c2ecf20Sopenharmony_ci		u64 val;
42298c2ecf20Sopenharmony_ci
42308c2ecf20Sopenharmony_ci		val  = FIELD_PREP(GITS_SGIR_VPEID, vpe->vpe_id);
42318c2ecf20Sopenharmony_ci		val |= FIELD_PREP(GITS_SGIR_VINTID, d->hwirq);
42328c2ecf20Sopenharmony_ci		writeq_relaxed(val, its->sgir_base + GITS_SGIR - SZ_128K);
42338c2ecf20Sopenharmony_ci	} else {
42348c2ecf20Sopenharmony_ci		its_configure_sgi(d, true);
42358c2ecf20Sopenharmony_ci	}
42368c2ecf20Sopenharmony_ci
42378c2ecf20Sopenharmony_ci	return 0;
42388c2ecf20Sopenharmony_ci}
42398c2ecf20Sopenharmony_ci
42408c2ecf20Sopenharmony_cistatic int its_sgi_get_irqchip_state(struct irq_data *d,
42418c2ecf20Sopenharmony_ci				     enum irqchip_irq_state which, bool *val)
42428c2ecf20Sopenharmony_ci{
42438c2ecf20Sopenharmony_ci	struct its_vpe *vpe = irq_data_get_irq_chip_data(d);
42448c2ecf20Sopenharmony_ci	void __iomem *base;
42458c2ecf20Sopenharmony_ci	unsigned long flags;
42468c2ecf20Sopenharmony_ci	u32 count = 1000000;	/* 1s! */
42478c2ecf20Sopenharmony_ci	u32 status;
42488c2ecf20Sopenharmony_ci	int cpu;
42498c2ecf20Sopenharmony_ci
42508c2ecf20Sopenharmony_ci	if (which != IRQCHIP_STATE_PENDING)
42518c2ecf20Sopenharmony_ci		return -EINVAL;
42528c2ecf20Sopenharmony_ci
42538c2ecf20Sopenharmony_ci	/*
42548c2ecf20Sopenharmony_ci	 * Locking galore! We can race against two different events:
42558c2ecf20Sopenharmony_ci	 *
42568c2ecf20Sopenharmony_ci	 * - Concurrent vPE affinity change: we must make sure it cannot
42578c2ecf20Sopenharmony_ci	 *   happen, or we'll talk to the wrong redistributor. This is
42588c2ecf20Sopenharmony_ci	 *   identical to what happens with vLPIs.
42598c2ecf20Sopenharmony_ci	 *
42608c2ecf20Sopenharmony_ci	 * - Concurrent VSGIPENDR access: As it involves accessing two
42618c2ecf20Sopenharmony_ci	 *   MMIO registers, this must be made atomic one way or another.
42628c2ecf20Sopenharmony_ci	 */
42638c2ecf20Sopenharmony_ci	cpu = vpe_to_cpuid_lock(vpe, &flags);
42648c2ecf20Sopenharmony_ci	raw_spin_lock(&gic_data_rdist_cpu(cpu)->rd_lock);
42658c2ecf20Sopenharmony_ci	base = gic_data_rdist_cpu(cpu)->rd_base + SZ_128K;
42668c2ecf20Sopenharmony_ci	writel_relaxed(vpe->vpe_id, base + GICR_VSGIR);
42678c2ecf20Sopenharmony_ci	do {
42688c2ecf20Sopenharmony_ci		status = readl_relaxed(base + GICR_VSGIPENDR);
42698c2ecf20Sopenharmony_ci		if (!(status & GICR_VSGIPENDR_BUSY))
42708c2ecf20Sopenharmony_ci			goto out;
42718c2ecf20Sopenharmony_ci
42728c2ecf20Sopenharmony_ci		count--;
42738c2ecf20Sopenharmony_ci		if (!count) {
42748c2ecf20Sopenharmony_ci			pr_err_ratelimited("Unable to get SGI status\n");
42758c2ecf20Sopenharmony_ci			goto out;
42768c2ecf20Sopenharmony_ci		}
42778c2ecf20Sopenharmony_ci		cpu_relax();
42788c2ecf20Sopenharmony_ci		udelay(1);
42798c2ecf20Sopenharmony_ci	} while (count);
42808c2ecf20Sopenharmony_ci
42818c2ecf20Sopenharmony_ciout:
42828c2ecf20Sopenharmony_ci	raw_spin_unlock(&gic_data_rdist_cpu(cpu)->rd_lock);
42838c2ecf20Sopenharmony_ci	vpe_to_cpuid_unlock(vpe, flags);
42848c2ecf20Sopenharmony_ci
42858c2ecf20Sopenharmony_ci	if (!count)
42868c2ecf20Sopenharmony_ci		return -ENXIO;
42878c2ecf20Sopenharmony_ci
42888c2ecf20Sopenharmony_ci	*val = !!(status & (1 << d->hwirq));
42898c2ecf20Sopenharmony_ci
42908c2ecf20Sopenharmony_ci	return 0;
42918c2ecf20Sopenharmony_ci}
42928c2ecf20Sopenharmony_ci
42938c2ecf20Sopenharmony_cistatic int its_sgi_set_vcpu_affinity(struct irq_data *d, void *vcpu_info)
42948c2ecf20Sopenharmony_ci{
42958c2ecf20Sopenharmony_ci	struct its_vpe *vpe = irq_data_get_irq_chip_data(d);
42968c2ecf20Sopenharmony_ci	struct its_cmd_info *info = vcpu_info;
42978c2ecf20Sopenharmony_ci
42988c2ecf20Sopenharmony_ci	switch (info->cmd_type) {
42998c2ecf20Sopenharmony_ci	case PROP_UPDATE_VSGI:
43008c2ecf20Sopenharmony_ci		vpe->sgi_config[d->hwirq].priority = info->priority;
43018c2ecf20Sopenharmony_ci		vpe->sgi_config[d->hwirq].group = info->group;
43028c2ecf20Sopenharmony_ci		its_configure_sgi(d, false);
43038c2ecf20Sopenharmony_ci		return 0;
43048c2ecf20Sopenharmony_ci
43058c2ecf20Sopenharmony_ci	default:
43068c2ecf20Sopenharmony_ci		return -EINVAL;
43078c2ecf20Sopenharmony_ci	}
43088c2ecf20Sopenharmony_ci}
43098c2ecf20Sopenharmony_ci
43108c2ecf20Sopenharmony_cistatic struct irq_chip its_sgi_irq_chip = {
43118c2ecf20Sopenharmony_ci	.name			= "GICv4.1-sgi",
43128c2ecf20Sopenharmony_ci	.irq_mask		= its_sgi_mask_irq,
43138c2ecf20Sopenharmony_ci	.irq_unmask		= its_sgi_unmask_irq,
43148c2ecf20Sopenharmony_ci	.irq_set_affinity	= its_sgi_set_affinity,
43158c2ecf20Sopenharmony_ci	.irq_set_irqchip_state	= its_sgi_set_irqchip_state,
43168c2ecf20Sopenharmony_ci	.irq_get_irqchip_state	= its_sgi_get_irqchip_state,
43178c2ecf20Sopenharmony_ci	.irq_set_vcpu_affinity	= its_sgi_set_vcpu_affinity,
43188c2ecf20Sopenharmony_ci};
43198c2ecf20Sopenharmony_ci
43208c2ecf20Sopenharmony_cistatic int its_sgi_irq_domain_alloc(struct irq_domain *domain,
43218c2ecf20Sopenharmony_ci				    unsigned int virq, unsigned int nr_irqs,
43228c2ecf20Sopenharmony_ci				    void *args)
43238c2ecf20Sopenharmony_ci{
43248c2ecf20Sopenharmony_ci	struct its_vpe *vpe = args;
43258c2ecf20Sopenharmony_ci	int i;
43268c2ecf20Sopenharmony_ci
43278c2ecf20Sopenharmony_ci	/* Yes, we do want 16 SGIs */
43288c2ecf20Sopenharmony_ci	WARN_ON(nr_irqs != 16);
43298c2ecf20Sopenharmony_ci
43308c2ecf20Sopenharmony_ci	for (i = 0; i < 16; i++) {
43318c2ecf20Sopenharmony_ci		vpe->sgi_config[i].priority = 0;
43328c2ecf20Sopenharmony_ci		vpe->sgi_config[i].enabled = false;
43338c2ecf20Sopenharmony_ci		vpe->sgi_config[i].group = false;
43348c2ecf20Sopenharmony_ci
43358c2ecf20Sopenharmony_ci		irq_domain_set_hwirq_and_chip(domain, virq + i, i,
43368c2ecf20Sopenharmony_ci					      &its_sgi_irq_chip, vpe);
43378c2ecf20Sopenharmony_ci		irq_set_status_flags(virq + i, IRQ_DISABLE_UNLAZY);
43388c2ecf20Sopenharmony_ci	}
43398c2ecf20Sopenharmony_ci
43408c2ecf20Sopenharmony_ci	return 0;
43418c2ecf20Sopenharmony_ci}
43428c2ecf20Sopenharmony_ci
43438c2ecf20Sopenharmony_cistatic void its_sgi_irq_domain_free(struct irq_domain *domain,
43448c2ecf20Sopenharmony_ci				    unsigned int virq,
43458c2ecf20Sopenharmony_ci				    unsigned int nr_irqs)
43468c2ecf20Sopenharmony_ci{
43478c2ecf20Sopenharmony_ci	/* Nothing to do */
43488c2ecf20Sopenharmony_ci}
43498c2ecf20Sopenharmony_ci
43508c2ecf20Sopenharmony_cistatic int its_sgi_irq_domain_activate(struct irq_domain *domain,
43518c2ecf20Sopenharmony_ci				       struct irq_data *d, bool reserve)
43528c2ecf20Sopenharmony_ci{
43538c2ecf20Sopenharmony_ci	/* Write out the initial SGI configuration */
43548c2ecf20Sopenharmony_ci	its_configure_sgi(d, false);
43558c2ecf20Sopenharmony_ci	return 0;
43568c2ecf20Sopenharmony_ci}
43578c2ecf20Sopenharmony_ci
43588c2ecf20Sopenharmony_cistatic void its_sgi_irq_domain_deactivate(struct irq_domain *domain,
43598c2ecf20Sopenharmony_ci					  struct irq_data *d)
43608c2ecf20Sopenharmony_ci{
43618c2ecf20Sopenharmony_ci	struct its_vpe *vpe = irq_data_get_irq_chip_data(d);
43628c2ecf20Sopenharmony_ci
43638c2ecf20Sopenharmony_ci	/*
43648c2ecf20Sopenharmony_ci	 * The VSGI command is awkward:
43658c2ecf20Sopenharmony_ci	 *
43668c2ecf20Sopenharmony_ci	 * - To change the configuration, CLEAR must be set to false,
43678c2ecf20Sopenharmony_ci	 *   leaving the pending bit unchanged.
43688c2ecf20Sopenharmony_ci	 * - To clear the pending bit, CLEAR must be set to true, leaving
43698c2ecf20Sopenharmony_ci	 *   the configuration unchanged.
43708c2ecf20Sopenharmony_ci	 *
43718c2ecf20Sopenharmony_ci	 * You just can't do both at once, hence the two commands below.
43728c2ecf20Sopenharmony_ci	 */
43738c2ecf20Sopenharmony_ci	vpe->sgi_config[d->hwirq].enabled = false;
43748c2ecf20Sopenharmony_ci	its_configure_sgi(d, false);
43758c2ecf20Sopenharmony_ci	its_configure_sgi(d, true);
43768c2ecf20Sopenharmony_ci}
43778c2ecf20Sopenharmony_ci
43788c2ecf20Sopenharmony_cistatic const struct irq_domain_ops its_sgi_domain_ops = {
43798c2ecf20Sopenharmony_ci	.alloc		= its_sgi_irq_domain_alloc,
43808c2ecf20Sopenharmony_ci	.free		= its_sgi_irq_domain_free,
43818c2ecf20Sopenharmony_ci	.activate	= its_sgi_irq_domain_activate,
43828c2ecf20Sopenharmony_ci	.deactivate	= its_sgi_irq_domain_deactivate,
43838c2ecf20Sopenharmony_ci};
43848c2ecf20Sopenharmony_ci
43858c2ecf20Sopenharmony_cistatic int its_vpe_id_alloc(void)
43868c2ecf20Sopenharmony_ci{
43878c2ecf20Sopenharmony_ci	return ida_simple_get(&its_vpeid_ida, 0, ITS_MAX_VPEID, GFP_KERNEL);
43888c2ecf20Sopenharmony_ci}
43898c2ecf20Sopenharmony_ci
43908c2ecf20Sopenharmony_cistatic void its_vpe_id_free(u16 id)
43918c2ecf20Sopenharmony_ci{
43928c2ecf20Sopenharmony_ci	ida_simple_remove(&its_vpeid_ida, id);
43938c2ecf20Sopenharmony_ci}
43948c2ecf20Sopenharmony_ci
43958c2ecf20Sopenharmony_cistatic int its_vpe_init(struct its_vpe *vpe)
43968c2ecf20Sopenharmony_ci{
43978c2ecf20Sopenharmony_ci	struct page *vpt_page;
43988c2ecf20Sopenharmony_ci	int vpe_id;
43998c2ecf20Sopenharmony_ci
44008c2ecf20Sopenharmony_ci	/* Allocate vpe_id */
44018c2ecf20Sopenharmony_ci	vpe_id = its_vpe_id_alloc();
44028c2ecf20Sopenharmony_ci	if (vpe_id < 0)
44038c2ecf20Sopenharmony_ci		return vpe_id;
44048c2ecf20Sopenharmony_ci
44058c2ecf20Sopenharmony_ci	/* Allocate VPT */
44068c2ecf20Sopenharmony_ci	vpt_page = its_allocate_pending_table(GFP_KERNEL);
44078c2ecf20Sopenharmony_ci	if (!vpt_page) {
44088c2ecf20Sopenharmony_ci		its_vpe_id_free(vpe_id);
44098c2ecf20Sopenharmony_ci		return -ENOMEM;
44108c2ecf20Sopenharmony_ci	}
44118c2ecf20Sopenharmony_ci
44128c2ecf20Sopenharmony_ci	if (!its_alloc_vpe_table(vpe_id)) {
44138c2ecf20Sopenharmony_ci		its_vpe_id_free(vpe_id);
44148c2ecf20Sopenharmony_ci		its_free_pending_table(vpt_page);
44158c2ecf20Sopenharmony_ci		return -ENOMEM;
44168c2ecf20Sopenharmony_ci	}
44178c2ecf20Sopenharmony_ci
44188c2ecf20Sopenharmony_ci	raw_spin_lock_init(&vpe->vpe_lock);
44198c2ecf20Sopenharmony_ci	vpe->vpe_id = vpe_id;
44208c2ecf20Sopenharmony_ci	vpe->vpt_page = vpt_page;
44218c2ecf20Sopenharmony_ci	if (gic_rdists->has_rvpeid)
44228c2ecf20Sopenharmony_ci		atomic_set(&vpe->vmapp_count, 0);
44238c2ecf20Sopenharmony_ci	else
44248c2ecf20Sopenharmony_ci		vpe->vpe_proxy_event = -1;
44258c2ecf20Sopenharmony_ci
44268c2ecf20Sopenharmony_ci	return 0;
44278c2ecf20Sopenharmony_ci}
44288c2ecf20Sopenharmony_ci
44298c2ecf20Sopenharmony_cistatic void its_vpe_teardown(struct its_vpe *vpe)
44308c2ecf20Sopenharmony_ci{
44318c2ecf20Sopenharmony_ci	its_vpe_db_proxy_unmap(vpe);
44328c2ecf20Sopenharmony_ci	its_vpe_id_free(vpe->vpe_id);
44338c2ecf20Sopenharmony_ci	its_free_pending_table(vpe->vpt_page);
44348c2ecf20Sopenharmony_ci}
44358c2ecf20Sopenharmony_ci
44368c2ecf20Sopenharmony_cistatic void its_vpe_irq_domain_free(struct irq_domain *domain,
44378c2ecf20Sopenharmony_ci				    unsigned int virq,
44388c2ecf20Sopenharmony_ci				    unsigned int nr_irqs)
44398c2ecf20Sopenharmony_ci{
44408c2ecf20Sopenharmony_ci	struct its_vm *vm = domain->host_data;
44418c2ecf20Sopenharmony_ci	int i;
44428c2ecf20Sopenharmony_ci
44438c2ecf20Sopenharmony_ci	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
44448c2ecf20Sopenharmony_ci
44458c2ecf20Sopenharmony_ci	for (i = 0; i < nr_irqs; i++) {
44468c2ecf20Sopenharmony_ci		struct irq_data *data = irq_domain_get_irq_data(domain,
44478c2ecf20Sopenharmony_ci								virq + i);
44488c2ecf20Sopenharmony_ci		struct its_vpe *vpe = irq_data_get_irq_chip_data(data);
44498c2ecf20Sopenharmony_ci
44508c2ecf20Sopenharmony_ci		BUG_ON(vm != vpe->its_vm);
44518c2ecf20Sopenharmony_ci
44528c2ecf20Sopenharmony_ci		clear_bit(data->hwirq, vm->db_bitmap);
44538c2ecf20Sopenharmony_ci		its_vpe_teardown(vpe);
44548c2ecf20Sopenharmony_ci		irq_domain_reset_irq_data(data);
44558c2ecf20Sopenharmony_ci	}
44568c2ecf20Sopenharmony_ci
44578c2ecf20Sopenharmony_ci	if (bitmap_empty(vm->db_bitmap, vm->nr_db_lpis)) {
44588c2ecf20Sopenharmony_ci		its_lpi_free(vm->db_bitmap, vm->db_lpi_base, vm->nr_db_lpis);
44598c2ecf20Sopenharmony_ci		its_free_prop_table(vm->vprop_page);
44608c2ecf20Sopenharmony_ci	}
44618c2ecf20Sopenharmony_ci}
44628c2ecf20Sopenharmony_ci
44638c2ecf20Sopenharmony_cistatic int its_vpe_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
44648c2ecf20Sopenharmony_ci				    unsigned int nr_irqs, void *args)
44658c2ecf20Sopenharmony_ci{
44668c2ecf20Sopenharmony_ci	struct irq_chip *irqchip = &its_vpe_irq_chip;
44678c2ecf20Sopenharmony_ci	struct its_vm *vm = args;
44688c2ecf20Sopenharmony_ci	unsigned long *bitmap;
44698c2ecf20Sopenharmony_ci	struct page *vprop_page;
44708c2ecf20Sopenharmony_ci	int base, nr_ids, i, err = 0;
44718c2ecf20Sopenharmony_ci
44728c2ecf20Sopenharmony_ci	BUG_ON(!vm);
44738c2ecf20Sopenharmony_ci
44748c2ecf20Sopenharmony_ci	bitmap = its_lpi_alloc(roundup_pow_of_two(nr_irqs), &base, &nr_ids);
44758c2ecf20Sopenharmony_ci	if (!bitmap)
44768c2ecf20Sopenharmony_ci		return -ENOMEM;
44778c2ecf20Sopenharmony_ci
44788c2ecf20Sopenharmony_ci	if (nr_ids < nr_irqs) {
44798c2ecf20Sopenharmony_ci		its_lpi_free(bitmap, base, nr_ids);
44808c2ecf20Sopenharmony_ci		return -ENOMEM;
44818c2ecf20Sopenharmony_ci	}
44828c2ecf20Sopenharmony_ci
44838c2ecf20Sopenharmony_ci	vprop_page = its_allocate_prop_table(GFP_KERNEL);
44848c2ecf20Sopenharmony_ci	if (!vprop_page) {
44858c2ecf20Sopenharmony_ci		its_lpi_free(bitmap, base, nr_ids);
44868c2ecf20Sopenharmony_ci		return -ENOMEM;
44878c2ecf20Sopenharmony_ci	}
44888c2ecf20Sopenharmony_ci
44898c2ecf20Sopenharmony_ci	vm->db_bitmap = bitmap;
44908c2ecf20Sopenharmony_ci	vm->db_lpi_base = base;
44918c2ecf20Sopenharmony_ci	vm->nr_db_lpis = nr_ids;
44928c2ecf20Sopenharmony_ci	vm->vprop_page = vprop_page;
44938c2ecf20Sopenharmony_ci
44948c2ecf20Sopenharmony_ci	if (gic_rdists->has_rvpeid)
44958c2ecf20Sopenharmony_ci		irqchip = &its_vpe_4_1_irq_chip;
44968c2ecf20Sopenharmony_ci
44978c2ecf20Sopenharmony_ci	for (i = 0; i < nr_irqs; i++) {
44988c2ecf20Sopenharmony_ci		vm->vpes[i]->vpe_db_lpi = base + i;
44998c2ecf20Sopenharmony_ci		err = its_vpe_init(vm->vpes[i]);
45008c2ecf20Sopenharmony_ci		if (err)
45018c2ecf20Sopenharmony_ci			break;
45028c2ecf20Sopenharmony_ci		err = its_irq_gic_domain_alloc(domain, virq + i,
45038c2ecf20Sopenharmony_ci					       vm->vpes[i]->vpe_db_lpi);
45048c2ecf20Sopenharmony_ci		if (err)
45058c2ecf20Sopenharmony_ci			break;
45068c2ecf20Sopenharmony_ci		irq_domain_set_hwirq_and_chip(domain, virq + i, i,
45078c2ecf20Sopenharmony_ci					      irqchip, vm->vpes[i]);
45088c2ecf20Sopenharmony_ci		set_bit(i, bitmap);
45098c2ecf20Sopenharmony_ci	}
45108c2ecf20Sopenharmony_ci
45118c2ecf20Sopenharmony_ci	if (err)
45128c2ecf20Sopenharmony_ci		its_vpe_irq_domain_free(domain, virq, i);
45138c2ecf20Sopenharmony_ci
45148c2ecf20Sopenharmony_ci	return err;
45158c2ecf20Sopenharmony_ci}
45168c2ecf20Sopenharmony_ci
45178c2ecf20Sopenharmony_cistatic int its_vpe_irq_domain_activate(struct irq_domain *domain,
45188c2ecf20Sopenharmony_ci				       struct irq_data *d, bool reserve)
45198c2ecf20Sopenharmony_ci{
45208c2ecf20Sopenharmony_ci	struct its_vpe *vpe = irq_data_get_irq_chip_data(d);
45218c2ecf20Sopenharmony_ci	struct its_node *its;
45228c2ecf20Sopenharmony_ci
45238c2ecf20Sopenharmony_ci	/*
45248c2ecf20Sopenharmony_ci	 * If we use the list map, we issue VMAPP on demand... Unless
45258c2ecf20Sopenharmony_ci	 * we're on a GICv4.1 and we eagerly map the VPE on all ITSs
45268c2ecf20Sopenharmony_ci	 * so that VSGIs can work.
45278c2ecf20Sopenharmony_ci	 */
45288c2ecf20Sopenharmony_ci	if (!gic_requires_eager_mapping())
45298c2ecf20Sopenharmony_ci		return 0;
45308c2ecf20Sopenharmony_ci
45318c2ecf20Sopenharmony_ci	/* Map the VPE to the first possible CPU */
45328c2ecf20Sopenharmony_ci	vpe->col_idx = cpumask_first(cpu_online_mask);
45338c2ecf20Sopenharmony_ci
45348c2ecf20Sopenharmony_ci	list_for_each_entry(its, &its_nodes, entry) {
45358c2ecf20Sopenharmony_ci		if (!is_v4(its))
45368c2ecf20Sopenharmony_ci			continue;
45378c2ecf20Sopenharmony_ci
45388c2ecf20Sopenharmony_ci		its_send_vmapp(its, vpe, true);
45398c2ecf20Sopenharmony_ci		its_send_vinvall(its, vpe);
45408c2ecf20Sopenharmony_ci	}
45418c2ecf20Sopenharmony_ci
45428c2ecf20Sopenharmony_ci	irq_data_update_effective_affinity(d, cpumask_of(vpe->col_idx));
45438c2ecf20Sopenharmony_ci
45448c2ecf20Sopenharmony_ci	return 0;
45458c2ecf20Sopenharmony_ci}
45468c2ecf20Sopenharmony_ci
45478c2ecf20Sopenharmony_cistatic void its_vpe_irq_domain_deactivate(struct irq_domain *domain,
45488c2ecf20Sopenharmony_ci					  struct irq_data *d)
45498c2ecf20Sopenharmony_ci{
45508c2ecf20Sopenharmony_ci	struct its_vpe *vpe = irq_data_get_irq_chip_data(d);
45518c2ecf20Sopenharmony_ci	struct its_node *its;
45528c2ecf20Sopenharmony_ci
45538c2ecf20Sopenharmony_ci	/*
45548c2ecf20Sopenharmony_ci	 * If we use the list map on GICv4.0, we unmap the VPE once no
45558c2ecf20Sopenharmony_ci	 * VLPIs are associated with the VM.
45568c2ecf20Sopenharmony_ci	 */
45578c2ecf20Sopenharmony_ci	if (!gic_requires_eager_mapping())
45588c2ecf20Sopenharmony_ci		return;
45598c2ecf20Sopenharmony_ci
45608c2ecf20Sopenharmony_ci	list_for_each_entry(its, &its_nodes, entry) {
45618c2ecf20Sopenharmony_ci		if (!is_v4(its))
45628c2ecf20Sopenharmony_ci			continue;
45638c2ecf20Sopenharmony_ci
45648c2ecf20Sopenharmony_ci		its_send_vmapp(its, vpe, false);
45658c2ecf20Sopenharmony_ci	}
45668c2ecf20Sopenharmony_ci}
45678c2ecf20Sopenharmony_ci
45688c2ecf20Sopenharmony_cistatic const struct irq_domain_ops its_vpe_domain_ops = {
45698c2ecf20Sopenharmony_ci	.alloc			= its_vpe_irq_domain_alloc,
45708c2ecf20Sopenharmony_ci	.free			= its_vpe_irq_domain_free,
45718c2ecf20Sopenharmony_ci	.activate		= its_vpe_irq_domain_activate,
45728c2ecf20Sopenharmony_ci	.deactivate		= its_vpe_irq_domain_deactivate,
45738c2ecf20Sopenharmony_ci};
45748c2ecf20Sopenharmony_ci
45758c2ecf20Sopenharmony_cistatic int its_force_quiescent(void __iomem *base)
45768c2ecf20Sopenharmony_ci{
45778c2ecf20Sopenharmony_ci	u32 count = 1000000;	/* 1s */
45788c2ecf20Sopenharmony_ci	u32 val;
45798c2ecf20Sopenharmony_ci
45808c2ecf20Sopenharmony_ci	val = readl_relaxed(base + GITS_CTLR);
45818c2ecf20Sopenharmony_ci	/*
45828c2ecf20Sopenharmony_ci	 * GIC architecture specification requires the ITS to be both
45838c2ecf20Sopenharmony_ci	 * disabled and quiescent for writes to GITS_BASER<n> or
45848c2ecf20Sopenharmony_ci	 * GITS_CBASER to not have UNPREDICTABLE results.
45858c2ecf20Sopenharmony_ci	 */
45868c2ecf20Sopenharmony_ci	if ((val & GITS_CTLR_QUIESCENT) && !(val & GITS_CTLR_ENABLE))
45878c2ecf20Sopenharmony_ci		return 0;
45888c2ecf20Sopenharmony_ci
45898c2ecf20Sopenharmony_ci	/* Disable the generation of all interrupts to this ITS */
45908c2ecf20Sopenharmony_ci	val &= ~(GITS_CTLR_ENABLE | GITS_CTLR_ImDe);
45918c2ecf20Sopenharmony_ci	writel_relaxed(val, base + GITS_CTLR);
45928c2ecf20Sopenharmony_ci
45938c2ecf20Sopenharmony_ci	/* Poll GITS_CTLR and wait until ITS becomes quiescent */
45948c2ecf20Sopenharmony_ci	while (1) {
45958c2ecf20Sopenharmony_ci		val = readl_relaxed(base + GITS_CTLR);
45968c2ecf20Sopenharmony_ci		if (val & GITS_CTLR_QUIESCENT)
45978c2ecf20Sopenharmony_ci			return 0;
45988c2ecf20Sopenharmony_ci
45998c2ecf20Sopenharmony_ci		count--;
46008c2ecf20Sopenharmony_ci		if (!count)
46018c2ecf20Sopenharmony_ci			return -EBUSY;
46028c2ecf20Sopenharmony_ci
46038c2ecf20Sopenharmony_ci		cpu_relax();
46048c2ecf20Sopenharmony_ci		udelay(1);
46058c2ecf20Sopenharmony_ci	}
46068c2ecf20Sopenharmony_ci}
46078c2ecf20Sopenharmony_ci
46088c2ecf20Sopenharmony_cistatic bool __maybe_unused its_enable_quirk_cavium_22375(void *data)
46098c2ecf20Sopenharmony_ci{
46108c2ecf20Sopenharmony_ci	struct its_node *its = data;
46118c2ecf20Sopenharmony_ci
46128c2ecf20Sopenharmony_ci	/* erratum 22375: only alloc 8MB table size (20 bits) */
46138c2ecf20Sopenharmony_ci	its->typer &= ~GITS_TYPER_DEVBITS;
46148c2ecf20Sopenharmony_ci	its->typer |= FIELD_PREP(GITS_TYPER_DEVBITS, 20 - 1);
46158c2ecf20Sopenharmony_ci	its->flags |= ITS_FLAGS_WORKAROUND_CAVIUM_22375;
46168c2ecf20Sopenharmony_ci
46178c2ecf20Sopenharmony_ci	return true;
46188c2ecf20Sopenharmony_ci}
46198c2ecf20Sopenharmony_ci
46208c2ecf20Sopenharmony_cistatic bool __maybe_unused its_enable_quirk_cavium_23144(void *data)
46218c2ecf20Sopenharmony_ci{
46228c2ecf20Sopenharmony_ci	struct its_node *its = data;
46238c2ecf20Sopenharmony_ci
46248c2ecf20Sopenharmony_ci	its->flags |= ITS_FLAGS_WORKAROUND_CAVIUM_23144;
46258c2ecf20Sopenharmony_ci
46268c2ecf20Sopenharmony_ci	return true;
46278c2ecf20Sopenharmony_ci}
46288c2ecf20Sopenharmony_ci
46298c2ecf20Sopenharmony_cistatic bool __maybe_unused its_enable_quirk_qdf2400_e0065(void *data)
46308c2ecf20Sopenharmony_ci{
46318c2ecf20Sopenharmony_ci	struct its_node *its = data;
46328c2ecf20Sopenharmony_ci
46338c2ecf20Sopenharmony_ci	/* On QDF2400, the size of the ITE is 16Bytes */
46348c2ecf20Sopenharmony_ci	its->typer &= ~GITS_TYPER_ITT_ENTRY_SIZE;
46358c2ecf20Sopenharmony_ci	its->typer |= FIELD_PREP(GITS_TYPER_ITT_ENTRY_SIZE, 16 - 1);
46368c2ecf20Sopenharmony_ci
46378c2ecf20Sopenharmony_ci	return true;
46388c2ecf20Sopenharmony_ci}
46398c2ecf20Sopenharmony_ci
46408c2ecf20Sopenharmony_cistatic u64 its_irq_get_msi_base_pre_its(struct its_device *its_dev)
46418c2ecf20Sopenharmony_ci{
46428c2ecf20Sopenharmony_ci	struct its_node *its = its_dev->its;
46438c2ecf20Sopenharmony_ci
46448c2ecf20Sopenharmony_ci	/*
46458c2ecf20Sopenharmony_ci	 * The Socionext Synquacer SoC has a so-called 'pre-ITS',
46468c2ecf20Sopenharmony_ci	 * which maps 32-bit writes targeted at a separate window of
46478c2ecf20Sopenharmony_ci	 * size '4 << device_id_bits' onto writes to GITS_TRANSLATER
46488c2ecf20Sopenharmony_ci	 * with device ID taken from bits [device_id_bits + 1:2] of
46498c2ecf20Sopenharmony_ci	 * the window offset.
46508c2ecf20Sopenharmony_ci	 */
46518c2ecf20Sopenharmony_ci	return its->pre_its_base + (its_dev->device_id << 2);
46528c2ecf20Sopenharmony_ci}
46538c2ecf20Sopenharmony_ci
46548c2ecf20Sopenharmony_cistatic bool __maybe_unused its_enable_quirk_socionext_synquacer(void *data)
46558c2ecf20Sopenharmony_ci{
46568c2ecf20Sopenharmony_ci	struct its_node *its = data;
46578c2ecf20Sopenharmony_ci	u32 pre_its_window[2];
46588c2ecf20Sopenharmony_ci	u32 ids;
46598c2ecf20Sopenharmony_ci
46608c2ecf20Sopenharmony_ci	if (!fwnode_property_read_u32_array(its->fwnode_handle,
46618c2ecf20Sopenharmony_ci					   "socionext,synquacer-pre-its",
46628c2ecf20Sopenharmony_ci					   pre_its_window,
46638c2ecf20Sopenharmony_ci					   ARRAY_SIZE(pre_its_window))) {
46648c2ecf20Sopenharmony_ci
46658c2ecf20Sopenharmony_ci		its->pre_its_base = pre_its_window[0];
46668c2ecf20Sopenharmony_ci		its->get_msi_base = its_irq_get_msi_base_pre_its;
46678c2ecf20Sopenharmony_ci
46688c2ecf20Sopenharmony_ci		ids = ilog2(pre_its_window[1]) - 2;
46698c2ecf20Sopenharmony_ci		if (device_ids(its) > ids) {
46708c2ecf20Sopenharmony_ci			its->typer &= ~GITS_TYPER_DEVBITS;
46718c2ecf20Sopenharmony_ci			its->typer |= FIELD_PREP(GITS_TYPER_DEVBITS, ids - 1);
46728c2ecf20Sopenharmony_ci		}
46738c2ecf20Sopenharmony_ci
46748c2ecf20Sopenharmony_ci		/* the pre-ITS breaks isolation, so disable MSI remapping */
46758c2ecf20Sopenharmony_ci		its->msi_domain_flags &= ~IRQ_DOMAIN_FLAG_MSI_REMAP;
46768c2ecf20Sopenharmony_ci		return true;
46778c2ecf20Sopenharmony_ci	}
46788c2ecf20Sopenharmony_ci	return false;
46798c2ecf20Sopenharmony_ci}
46808c2ecf20Sopenharmony_ci
46818c2ecf20Sopenharmony_cistatic bool __maybe_unused its_enable_quirk_hip07_161600802(void *data)
46828c2ecf20Sopenharmony_ci{
46838c2ecf20Sopenharmony_ci	struct its_node *its = data;
46848c2ecf20Sopenharmony_ci
46858c2ecf20Sopenharmony_ci	/*
46868c2ecf20Sopenharmony_ci	 * Hip07 insists on using the wrong address for the VLPI
46878c2ecf20Sopenharmony_ci	 * page. Trick it into doing the right thing...
46888c2ecf20Sopenharmony_ci	 */
46898c2ecf20Sopenharmony_ci	its->vlpi_redist_offset = SZ_128K;
46908c2ecf20Sopenharmony_ci	return true;
46918c2ecf20Sopenharmony_ci}
46928c2ecf20Sopenharmony_ci
46938c2ecf20Sopenharmony_cistatic const struct gic_quirk its_quirks[] = {
46948c2ecf20Sopenharmony_ci#ifdef CONFIG_CAVIUM_ERRATUM_22375
46958c2ecf20Sopenharmony_ci	{
46968c2ecf20Sopenharmony_ci		.desc	= "ITS: Cavium errata 22375, 24313",
46978c2ecf20Sopenharmony_ci		.iidr	= 0xa100034c,	/* ThunderX pass 1.x */
46988c2ecf20Sopenharmony_ci		.mask	= 0xffff0fff,
46998c2ecf20Sopenharmony_ci		.init	= its_enable_quirk_cavium_22375,
47008c2ecf20Sopenharmony_ci	},
47018c2ecf20Sopenharmony_ci#endif
47028c2ecf20Sopenharmony_ci#ifdef CONFIG_CAVIUM_ERRATUM_23144
47038c2ecf20Sopenharmony_ci	{
47048c2ecf20Sopenharmony_ci		.desc	= "ITS: Cavium erratum 23144",
47058c2ecf20Sopenharmony_ci		.iidr	= 0xa100034c,	/* ThunderX pass 1.x */
47068c2ecf20Sopenharmony_ci		.mask	= 0xffff0fff,
47078c2ecf20Sopenharmony_ci		.init	= its_enable_quirk_cavium_23144,
47088c2ecf20Sopenharmony_ci	},
47098c2ecf20Sopenharmony_ci#endif
47108c2ecf20Sopenharmony_ci#ifdef CONFIG_QCOM_QDF2400_ERRATUM_0065
47118c2ecf20Sopenharmony_ci	{
47128c2ecf20Sopenharmony_ci		.desc	= "ITS: QDF2400 erratum 0065",
47138c2ecf20Sopenharmony_ci		.iidr	= 0x00001070, /* QDF2400 ITS rev 1.x */
47148c2ecf20Sopenharmony_ci		.mask	= 0xffffffff,
47158c2ecf20Sopenharmony_ci		.init	= its_enable_quirk_qdf2400_e0065,
47168c2ecf20Sopenharmony_ci	},
47178c2ecf20Sopenharmony_ci#endif
47188c2ecf20Sopenharmony_ci#ifdef CONFIG_SOCIONEXT_SYNQUACER_PREITS
47198c2ecf20Sopenharmony_ci	{
47208c2ecf20Sopenharmony_ci		/*
47218c2ecf20Sopenharmony_ci		 * The Socionext Synquacer SoC incorporates ARM's own GIC-500
47228c2ecf20Sopenharmony_ci		 * implementation, but with a 'pre-ITS' added that requires
47238c2ecf20Sopenharmony_ci		 * special handling in software.
47248c2ecf20Sopenharmony_ci		 */
47258c2ecf20Sopenharmony_ci		.desc	= "ITS: Socionext Synquacer pre-ITS",
47268c2ecf20Sopenharmony_ci		.iidr	= 0x0001143b,
47278c2ecf20Sopenharmony_ci		.mask	= 0xffffffff,
47288c2ecf20Sopenharmony_ci		.init	= its_enable_quirk_socionext_synquacer,
47298c2ecf20Sopenharmony_ci	},
47308c2ecf20Sopenharmony_ci#endif
47318c2ecf20Sopenharmony_ci#ifdef CONFIG_HISILICON_ERRATUM_161600802
47328c2ecf20Sopenharmony_ci	{
47338c2ecf20Sopenharmony_ci		.desc	= "ITS: Hip07 erratum 161600802",
47348c2ecf20Sopenharmony_ci		.iidr	= 0x00000004,
47358c2ecf20Sopenharmony_ci		.mask	= 0xffffffff,
47368c2ecf20Sopenharmony_ci		.init	= its_enable_quirk_hip07_161600802,
47378c2ecf20Sopenharmony_ci	},
47388c2ecf20Sopenharmony_ci#endif
47398c2ecf20Sopenharmony_ci	{
47408c2ecf20Sopenharmony_ci	}
47418c2ecf20Sopenharmony_ci};
47428c2ecf20Sopenharmony_ci
47438c2ecf20Sopenharmony_cistatic void its_enable_quirks(struct its_node *its)
47448c2ecf20Sopenharmony_ci{
47458c2ecf20Sopenharmony_ci	u32 iidr = readl_relaxed(its->base + GITS_IIDR);
47468c2ecf20Sopenharmony_ci
47478c2ecf20Sopenharmony_ci	gic_enable_quirks(iidr, its_quirks, its);
47488c2ecf20Sopenharmony_ci}
47498c2ecf20Sopenharmony_ci
47508c2ecf20Sopenharmony_cistatic int its_save_disable(void)
47518c2ecf20Sopenharmony_ci{
47528c2ecf20Sopenharmony_ci	struct its_node *its;
47538c2ecf20Sopenharmony_ci	int err = 0;
47548c2ecf20Sopenharmony_ci
47558c2ecf20Sopenharmony_ci	raw_spin_lock(&its_lock);
47568c2ecf20Sopenharmony_ci	list_for_each_entry(its, &its_nodes, entry) {
47578c2ecf20Sopenharmony_ci		void __iomem *base;
47588c2ecf20Sopenharmony_ci
47598c2ecf20Sopenharmony_ci		base = its->base;
47608c2ecf20Sopenharmony_ci		its->ctlr_save = readl_relaxed(base + GITS_CTLR);
47618c2ecf20Sopenharmony_ci		err = its_force_quiescent(base);
47628c2ecf20Sopenharmony_ci		if (err) {
47638c2ecf20Sopenharmony_ci			pr_err("ITS@%pa: failed to quiesce: %d\n",
47648c2ecf20Sopenharmony_ci			       &its->phys_base, err);
47658c2ecf20Sopenharmony_ci			writel_relaxed(its->ctlr_save, base + GITS_CTLR);
47668c2ecf20Sopenharmony_ci			goto err;
47678c2ecf20Sopenharmony_ci		}
47688c2ecf20Sopenharmony_ci
47698c2ecf20Sopenharmony_ci		its->cbaser_save = gits_read_cbaser(base + GITS_CBASER);
47708c2ecf20Sopenharmony_ci	}
47718c2ecf20Sopenharmony_ci
47728c2ecf20Sopenharmony_cierr:
47738c2ecf20Sopenharmony_ci	if (err) {
47748c2ecf20Sopenharmony_ci		list_for_each_entry_continue_reverse(its, &its_nodes, entry) {
47758c2ecf20Sopenharmony_ci			void __iomem *base;
47768c2ecf20Sopenharmony_ci
47778c2ecf20Sopenharmony_ci			base = its->base;
47788c2ecf20Sopenharmony_ci			writel_relaxed(its->ctlr_save, base + GITS_CTLR);
47798c2ecf20Sopenharmony_ci		}
47808c2ecf20Sopenharmony_ci	}
47818c2ecf20Sopenharmony_ci	raw_spin_unlock(&its_lock);
47828c2ecf20Sopenharmony_ci
47838c2ecf20Sopenharmony_ci	return err;
47848c2ecf20Sopenharmony_ci}
47858c2ecf20Sopenharmony_ci
47868c2ecf20Sopenharmony_cistatic void its_restore_enable(void)
47878c2ecf20Sopenharmony_ci{
47888c2ecf20Sopenharmony_ci	struct its_node *its;
47898c2ecf20Sopenharmony_ci	int ret;
47908c2ecf20Sopenharmony_ci
47918c2ecf20Sopenharmony_ci	raw_spin_lock(&its_lock);
47928c2ecf20Sopenharmony_ci	list_for_each_entry(its, &its_nodes, entry) {
47938c2ecf20Sopenharmony_ci		void __iomem *base;
47948c2ecf20Sopenharmony_ci		int i;
47958c2ecf20Sopenharmony_ci
47968c2ecf20Sopenharmony_ci		base = its->base;
47978c2ecf20Sopenharmony_ci
47988c2ecf20Sopenharmony_ci		/*
47998c2ecf20Sopenharmony_ci		 * Make sure that the ITS is disabled. If it fails to quiesce,
48008c2ecf20Sopenharmony_ci		 * don't restore it since writing to CBASER or BASER<n>
48018c2ecf20Sopenharmony_ci		 * registers is undefined according to the GIC v3 ITS
48028c2ecf20Sopenharmony_ci		 * Specification.
48038c2ecf20Sopenharmony_ci		 *
48048c2ecf20Sopenharmony_ci		 * Firmware resuming with the ITS enabled is terminally broken.
48058c2ecf20Sopenharmony_ci		 */
48068c2ecf20Sopenharmony_ci		WARN_ON(readl_relaxed(base + GITS_CTLR) & GITS_CTLR_ENABLE);
48078c2ecf20Sopenharmony_ci		ret = its_force_quiescent(base);
48088c2ecf20Sopenharmony_ci		if (ret) {
48098c2ecf20Sopenharmony_ci			pr_err("ITS@%pa: failed to quiesce on resume: %d\n",
48108c2ecf20Sopenharmony_ci			       &its->phys_base, ret);
48118c2ecf20Sopenharmony_ci			continue;
48128c2ecf20Sopenharmony_ci		}
48138c2ecf20Sopenharmony_ci
48148c2ecf20Sopenharmony_ci		gits_write_cbaser(its->cbaser_save, base + GITS_CBASER);
48158c2ecf20Sopenharmony_ci
48168c2ecf20Sopenharmony_ci		/*
48178c2ecf20Sopenharmony_ci		 * Writing CBASER resets CREADR to 0, so make CWRITER and
48188c2ecf20Sopenharmony_ci		 * cmd_write line up with it.
48198c2ecf20Sopenharmony_ci		 */
48208c2ecf20Sopenharmony_ci		its->cmd_write = its->cmd_base;
48218c2ecf20Sopenharmony_ci		gits_write_cwriter(0, base + GITS_CWRITER);
48228c2ecf20Sopenharmony_ci
48238c2ecf20Sopenharmony_ci		/* Restore GITS_BASER from the value cache. */
48248c2ecf20Sopenharmony_ci		for (i = 0; i < GITS_BASER_NR_REGS; i++) {
48258c2ecf20Sopenharmony_ci			struct its_baser *baser = &its->tables[i];
48268c2ecf20Sopenharmony_ci
48278c2ecf20Sopenharmony_ci			if (!(baser->val & GITS_BASER_VALID))
48288c2ecf20Sopenharmony_ci				continue;
48298c2ecf20Sopenharmony_ci
48308c2ecf20Sopenharmony_ci			its_write_baser(its, baser, baser->val);
48318c2ecf20Sopenharmony_ci		}
48328c2ecf20Sopenharmony_ci		writel_relaxed(its->ctlr_save, base + GITS_CTLR);
48338c2ecf20Sopenharmony_ci
48348c2ecf20Sopenharmony_ci		/*
48358c2ecf20Sopenharmony_ci		 * Reinit the collection if it's stored in the ITS. This is
48368c2ecf20Sopenharmony_ci		 * indicated by the col_id being less than the HCC field.
48378c2ecf20Sopenharmony_ci		 * CID < HCC as specified in the GIC v3 Documentation.
48388c2ecf20Sopenharmony_ci		 */
48398c2ecf20Sopenharmony_ci		if (its->collections[smp_processor_id()].col_id <
48408c2ecf20Sopenharmony_ci		    GITS_TYPER_HCC(gic_read_typer(base + GITS_TYPER)))
48418c2ecf20Sopenharmony_ci			its_cpu_init_collection(its);
48428c2ecf20Sopenharmony_ci	}
48438c2ecf20Sopenharmony_ci	raw_spin_unlock(&its_lock);
48448c2ecf20Sopenharmony_ci}
48458c2ecf20Sopenharmony_ci
48468c2ecf20Sopenharmony_cistatic struct syscore_ops its_syscore_ops = {
48478c2ecf20Sopenharmony_ci	.suspend = its_save_disable,
48488c2ecf20Sopenharmony_ci	.resume = its_restore_enable,
48498c2ecf20Sopenharmony_ci};
48508c2ecf20Sopenharmony_ci
48518c2ecf20Sopenharmony_cistatic int its_init_domain(struct fwnode_handle *handle, struct its_node *its)
48528c2ecf20Sopenharmony_ci{
48538c2ecf20Sopenharmony_ci	struct irq_domain *inner_domain;
48548c2ecf20Sopenharmony_ci	struct msi_domain_info *info;
48558c2ecf20Sopenharmony_ci
48568c2ecf20Sopenharmony_ci	info = kzalloc(sizeof(*info), GFP_KERNEL);
48578c2ecf20Sopenharmony_ci	if (!info)
48588c2ecf20Sopenharmony_ci		return -ENOMEM;
48598c2ecf20Sopenharmony_ci
48608c2ecf20Sopenharmony_ci	inner_domain = irq_domain_create_tree(handle, &its_domain_ops, its);
48618c2ecf20Sopenharmony_ci	if (!inner_domain) {
48628c2ecf20Sopenharmony_ci		kfree(info);
48638c2ecf20Sopenharmony_ci		return -ENOMEM;
48648c2ecf20Sopenharmony_ci	}
48658c2ecf20Sopenharmony_ci
48668c2ecf20Sopenharmony_ci	inner_domain->parent = its_parent;
48678c2ecf20Sopenharmony_ci	irq_domain_update_bus_token(inner_domain, DOMAIN_BUS_NEXUS);
48688c2ecf20Sopenharmony_ci	inner_domain->flags |= its->msi_domain_flags;
48698c2ecf20Sopenharmony_ci	info->ops = &its_msi_domain_ops;
48708c2ecf20Sopenharmony_ci	info->data = its;
48718c2ecf20Sopenharmony_ci	inner_domain->host_data = info;
48728c2ecf20Sopenharmony_ci
48738c2ecf20Sopenharmony_ci	return 0;
48748c2ecf20Sopenharmony_ci}
48758c2ecf20Sopenharmony_ci
48768c2ecf20Sopenharmony_cistatic int its_init_vpe_domain(void)
48778c2ecf20Sopenharmony_ci{
48788c2ecf20Sopenharmony_ci	struct its_node *its;
48798c2ecf20Sopenharmony_ci	u32 devid;
48808c2ecf20Sopenharmony_ci	int entries;
48818c2ecf20Sopenharmony_ci
48828c2ecf20Sopenharmony_ci	if (gic_rdists->has_direct_lpi) {
48838c2ecf20Sopenharmony_ci		pr_info("ITS: Using DirectLPI for VPE invalidation\n");
48848c2ecf20Sopenharmony_ci		return 0;
48858c2ecf20Sopenharmony_ci	}
48868c2ecf20Sopenharmony_ci
48878c2ecf20Sopenharmony_ci	/* Any ITS will do, even if not v4 */
48888c2ecf20Sopenharmony_ci	its = list_first_entry(&its_nodes, struct its_node, entry);
48898c2ecf20Sopenharmony_ci
48908c2ecf20Sopenharmony_ci	entries = roundup_pow_of_two(nr_cpu_ids);
48918c2ecf20Sopenharmony_ci	vpe_proxy.vpes = kcalloc(entries, sizeof(*vpe_proxy.vpes),
48928c2ecf20Sopenharmony_ci				 GFP_KERNEL);
48938c2ecf20Sopenharmony_ci	if (!vpe_proxy.vpes) {
48948c2ecf20Sopenharmony_ci		pr_err("ITS: Can't allocate GICv4 proxy device array\n");
48958c2ecf20Sopenharmony_ci		return -ENOMEM;
48968c2ecf20Sopenharmony_ci	}
48978c2ecf20Sopenharmony_ci
48988c2ecf20Sopenharmony_ci	/* Use the last possible DevID */
48998c2ecf20Sopenharmony_ci	devid = GENMASK(device_ids(its) - 1, 0);
49008c2ecf20Sopenharmony_ci	vpe_proxy.dev = its_create_device(its, devid, entries, false);
49018c2ecf20Sopenharmony_ci	if (!vpe_proxy.dev) {
49028c2ecf20Sopenharmony_ci		kfree(vpe_proxy.vpes);
49038c2ecf20Sopenharmony_ci		pr_err("ITS: Can't allocate GICv4 proxy device\n");
49048c2ecf20Sopenharmony_ci		return -ENOMEM;
49058c2ecf20Sopenharmony_ci	}
49068c2ecf20Sopenharmony_ci
49078c2ecf20Sopenharmony_ci	BUG_ON(entries > vpe_proxy.dev->nr_ites);
49088c2ecf20Sopenharmony_ci
49098c2ecf20Sopenharmony_ci	raw_spin_lock_init(&vpe_proxy.lock);
49108c2ecf20Sopenharmony_ci	vpe_proxy.next_victim = 0;
49118c2ecf20Sopenharmony_ci	pr_info("ITS: Allocated DevID %x as GICv4 proxy device (%d slots)\n",
49128c2ecf20Sopenharmony_ci		devid, vpe_proxy.dev->nr_ites);
49138c2ecf20Sopenharmony_ci
49148c2ecf20Sopenharmony_ci	return 0;
49158c2ecf20Sopenharmony_ci}
49168c2ecf20Sopenharmony_ci
49178c2ecf20Sopenharmony_cistatic int __init its_compute_its_list_map(struct resource *res,
49188c2ecf20Sopenharmony_ci					   void __iomem *its_base)
49198c2ecf20Sopenharmony_ci{
49208c2ecf20Sopenharmony_ci	int its_number;
49218c2ecf20Sopenharmony_ci	u32 ctlr;
49228c2ecf20Sopenharmony_ci
49238c2ecf20Sopenharmony_ci	/*
49248c2ecf20Sopenharmony_ci	 * This is assumed to be done early enough that we're
49258c2ecf20Sopenharmony_ci	 * guaranteed to be single-threaded, hence no
49268c2ecf20Sopenharmony_ci	 * locking. Should this change, we should address
49278c2ecf20Sopenharmony_ci	 * this.
49288c2ecf20Sopenharmony_ci	 */
49298c2ecf20Sopenharmony_ci	its_number = find_first_zero_bit(&its_list_map, GICv4_ITS_LIST_MAX);
49308c2ecf20Sopenharmony_ci	if (its_number >= GICv4_ITS_LIST_MAX) {
49318c2ecf20Sopenharmony_ci		pr_err("ITS@%pa: No ITSList entry available!\n",
49328c2ecf20Sopenharmony_ci		       &res->start);
49338c2ecf20Sopenharmony_ci		return -EINVAL;
49348c2ecf20Sopenharmony_ci	}
49358c2ecf20Sopenharmony_ci
49368c2ecf20Sopenharmony_ci	ctlr = readl_relaxed(its_base + GITS_CTLR);
49378c2ecf20Sopenharmony_ci	ctlr &= ~GITS_CTLR_ITS_NUMBER;
49388c2ecf20Sopenharmony_ci	ctlr |= its_number << GITS_CTLR_ITS_NUMBER_SHIFT;
49398c2ecf20Sopenharmony_ci	writel_relaxed(ctlr, its_base + GITS_CTLR);
49408c2ecf20Sopenharmony_ci	ctlr = readl_relaxed(its_base + GITS_CTLR);
49418c2ecf20Sopenharmony_ci	if ((ctlr & GITS_CTLR_ITS_NUMBER) != (its_number << GITS_CTLR_ITS_NUMBER_SHIFT)) {
49428c2ecf20Sopenharmony_ci		its_number = ctlr & GITS_CTLR_ITS_NUMBER;
49438c2ecf20Sopenharmony_ci		its_number >>= GITS_CTLR_ITS_NUMBER_SHIFT;
49448c2ecf20Sopenharmony_ci	}
49458c2ecf20Sopenharmony_ci
49468c2ecf20Sopenharmony_ci	if (test_and_set_bit(its_number, &its_list_map)) {
49478c2ecf20Sopenharmony_ci		pr_err("ITS@%pa: Duplicate ITSList entry %d\n",
49488c2ecf20Sopenharmony_ci		       &res->start, its_number);
49498c2ecf20Sopenharmony_ci		return -EINVAL;
49508c2ecf20Sopenharmony_ci	}
49518c2ecf20Sopenharmony_ci
49528c2ecf20Sopenharmony_ci	return its_number;
49538c2ecf20Sopenharmony_ci}
49548c2ecf20Sopenharmony_ci
49558c2ecf20Sopenharmony_cistatic int __init its_probe_one(struct resource *res,
49568c2ecf20Sopenharmony_ci				struct fwnode_handle *handle, int numa_node)
49578c2ecf20Sopenharmony_ci{
49588c2ecf20Sopenharmony_ci	struct its_node *its;
49598c2ecf20Sopenharmony_ci	void __iomem *its_base;
49608c2ecf20Sopenharmony_ci	u32 val, ctlr;
49618c2ecf20Sopenharmony_ci	u64 baser, tmp, typer;
49628c2ecf20Sopenharmony_ci	struct page *page;
49638c2ecf20Sopenharmony_ci	int err;
49648c2ecf20Sopenharmony_ci
49658c2ecf20Sopenharmony_ci	its_base = ioremap(res->start, SZ_64K);
49668c2ecf20Sopenharmony_ci	if (!its_base) {
49678c2ecf20Sopenharmony_ci		pr_warn("ITS@%pa: Unable to map ITS registers\n", &res->start);
49688c2ecf20Sopenharmony_ci		return -ENOMEM;
49698c2ecf20Sopenharmony_ci	}
49708c2ecf20Sopenharmony_ci
49718c2ecf20Sopenharmony_ci	val = readl_relaxed(its_base + GITS_PIDR2) & GIC_PIDR2_ARCH_MASK;
49728c2ecf20Sopenharmony_ci	if (val != 0x30 && val != 0x40) {
49738c2ecf20Sopenharmony_ci		pr_warn("ITS@%pa: No ITS detected, giving up\n", &res->start);
49748c2ecf20Sopenharmony_ci		err = -ENODEV;
49758c2ecf20Sopenharmony_ci		goto out_unmap;
49768c2ecf20Sopenharmony_ci	}
49778c2ecf20Sopenharmony_ci
49788c2ecf20Sopenharmony_ci	err = its_force_quiescent(its_base);
49798c2ecf20Sopenharmony_ci	if (err) {
49808c2ecf20Sopenharmony_ci		pr_warn("ITS@%pa: Failed to quiesce, giving up\n", &res->start);
49818c2ecf20Sopenharmony_ci		goto out_unmap;
49828c2ecf20Sopenharmony_ci	}
49838c2ecf20Sopenharmony_ci
49848c2ecf20Sopenharmony_ci	pr_info("ITS %pR\n", res);
49858c2ecf20Sopenharmony_ci
49868c2ecf20Sopenharmony_ci	its = kzalloc(sizeof(*its), GFP_KERNEL);
49878c2ecf20Sopenharmony_ci	if (!its) {
49888c2ecf20Sopenharmony_ci		err = -ENOMEM;
49898c2ecf20Sopenharmony_ci		goto out_unmap;
49908c2ecf20Sopenharmony_ci	}
49918c2ecf20Sopenharmony_ci
49928c2ecf20Sopenharmony_ci	raw_spin_lock_init(&its->lock);
49938c2ecf20Sopenharmony_ci	mutex_init(&its->dev_alloc_lock);
49948c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&its->entry);
49958c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&its->its_device_list);
49968c2ecf20Sopenharmony_ci	typer = gic_read_typer(its_base + GITS_TYPER);
49978c2ecf20Sopenharmony_ci	its->typer = typer;
49988c2ecf20Sopenharmony_ci	its->base = its_base;
49998c2ecf20Sopenharmony_ci	its->phys_base = res->start;
50008c2ecf20Sopenharmony_ci	if (is_v4(its)) {
50018c2ecf20Sopenharmony_ci		if (!(typer & GITS_TYPER_VMOVP)) {
50028c2ecf20Sopenharmony_ci			err = its_compute_its_list_map(res, its_base);
50038c2ecf20Sopenharmony_ci			if (err < 0)
50048c2ecf20Sopenharmony_ci				goto out_free_its;
50058c2ecf20Sopenharmony_ci
50068c2ecf20Sopenharmony_ci			its->list_nr = err;
50078c2ecf20Sopenharmony_ci
50088c2ecf20Sopenharmony_ci			pr_info("ITS@%pa: Using ITS number %d\n",
50098c2ecf20Sopenharmony_ci				&res->start, err);
50108c2ecf20Sopenharmony_ci		} else {
50118c2ecf20Sopenharmony_ci			pr_info("ITS@%pa: Single VMOVP capable\n", &res->start);
50128c2ecf20Sopenharmony_ci		}
50138c2ecf20Sopenharmony_ci
50148c2ecf20Sopenharmony_ci		if (is_v4_1(its)) {
50158c2ecf20Sopenharmony_ci			u32 svpet = FIELD_GET(GITS_TYPER_SVPET, typer);
50168c2ecf20Sopenharmony_ci
50178c2ecf20Sopenharmony_ci			its->sgir_base = ioremap(res->start + SZ_128K, SZ_64K);
50188c2ecf20Sopenharmony_ci			if (!its->sgir_base) {
50198c2ecf20Sopenharmony_ci				err = -ENOMEM;
50208c2ecf20Sopenharmony_ci				goto out_free_its;
50218c2ecf20Sopenharmony_ci			}
50228c2ecf20Sopenharmony_ci
50238c2ecf20Sopenharmony_ci			its->mpidr = readl_relaxed(its_base + GITS_MPIDR);
50248c2ecf20Sopenharmony_ci
50258c2ecf20Sopenharmony_ci			pr_info("ITS@%pa: Using GICv4.1 mode %08x %08x\n",
50268c2ecf20Sopenharmony_ci				&res->start, its->mpidr, svpet);
50278c2ecf20Sopenharmony_ci		}
50288c2ecf20Sopenharmony_ci	}
50298c2ecf20Sopenharmony_ci
50308c2ecf20Sopenharmony_ci	its->numa_node = numa_node;
50318c2ecf20Sopenharmony_ci
50328c2ecf20Sopenharmony_ci	page = alloc_pages_node(its->numa_node, GFP_KERNEL | __GFP_ZERO,
50338c2ecf20Sopenharmony_ci				get_order(ITS_CMD_QUEUE_SZ));
50348c2ecf20Sopenharmony_ci	if (!page) {
50358c2ecf20Sopenharmony_ci		err = -ENOMEM;
50368c2ecf20Sopenharmony_ci		goto out_unmap_sgir;
50378c2ecf20Sopenharmony_ci	}
50388c2ecf20Sopenharmony_ci	its->cmd_base = (void *)page_address(page);
50398c2ecf20Sopenharmony_ci	its->cmd_write = its->cmd_base;
50408c2ecf20Sopenharmony_ci	its->fwnode_handle = handle;
50418c2ecf20Sopenharmony_ci	its->get_msi_base = its_irq_get_msi_base;
50428c2ecf20Sopenharmony_ci	its->msi_domain_flags = IRQ_DOMAIN_FLAG_MSI_REMAP;
50438c2ecf20Sopenharmony_ci
50448c2ecf20Sopenharmony_ci	its_enable_quirks(its);
50458c2ecf20Sopenharmony_ci
50468c2ecf20Sopenharmony_ci	err = its_alloc_tables(its);
50478c2ecf20Sopenharmony_ci	if (err)
50488c2ecf20Sopenharmony_ci		goto out_free_cmd;
50498c2ecf20Sopenharmony_ci
50508c2ecf20Sopenharmony_ci	err = its_alloc_collections(its);
50518c2ecf20Sopenharmony_ci	if (err)
50528c2ecf20Sopenharmony_ci		goto out_free_tables;
50538c2ecf20Sopenharmony_ci
50548c2ecf20Sopenharmony_ci	baser = (virt_to_phys(its->cmd_base)	|
50558c2ecf20Sopenharmony_ci		 GITS_CBASER_RaWaWb		|
50568c2ecf20Sopenharmony_ci		 GITS_CBASER_InnerShareable	|
50578c2ecf20Sopenharmony_ci		 (ITS_CMD_QUEUE_SZ / SZ_4K - 1)	|
50588c2ecf20Sopenharmony_ci		 GITS_CBASER_VALID);
50598c2ecf20Sopenharmony_ci
50608c2ecf20Sopenharmony_ci	gits_write_cbaser(baser, its->base + GITS_CBASER);
50618c2ecf20Sopenharmony_ci	tmp = gits_read_cbaser(its->base + GITS_CBASER);
50628c2ecf20Sopenharmony_ci
50638c2ecf20Sopenharmony_ci	if ((tmp ^ baser) & GITS_CBASER_SHAREABILITY_MASK) {
50648c2ecf20Sopenharmony_ci		if (!(tmp & GITS_CBASER_SHAREABILITY_MASK)) {
50658c2ecf20Sopenharmony_ci			/*
50668c2ecf20Sopenharmony_ci			 * The HW reports non-shareable, we must
50678c2ecf20Sopenharmony_ci			 * remove the cacheability attributes as
50688c2ecf20Sopenharmony_ci			 * well.
50698c2ecf20Sopenharmony_ci			 */
50708c2ecf20Sopenharmony_ci			baser &= ~(GITS_CBASER_SHAREABILITY_MASK |
50718c2ecf20Sopenharmony_ci				   GITS_CBASER_CACHEABILITY_MASK);
50728c2ecf20Sopenharmony_ci			baser |= GITS_CBASER_nC;
50738c2ecf20Sopenharmony_ci			gits_write_cbaser(baser, its->base + GITS_CBASER);
50748c2ecf20Sopenharmony_ci		}
50758c2ecf20Sopenharmony_ci		pr_info("ITS: using cache flushing for cmd queue\n");
50768c2ecf20Sopenharmony_ci		its->flags |= ITS_FLAGS_CMDQ_NEEDS_FLUSHING;
50778c2ecf20Sopenharmony_ci	}
50788c2ecf20Sopenharmony_ci
50798c2ecf20Sopenharmony_ci	gits_write_cwriter(0, its->base + GITS_CWRITER);
50808c2ecf20Sopenharmony_ci	ctlr = readl_relaxed(its->base + GITS_CTLR);
50818c2ecf20Sopenharmony_ci	ctlr |= GITS_CTLR_ENABLE;
50828c2ecf20Sopenharmony_ci	if (is_v4(its))
50838c2ecf20Sopenharmony_ci		ctlr |= GITS_CTLR_ImDe;
50848c2ecf20Sopenharmony_ci	writel_relaxed(ctlr, its->base + GITS_CTLR);
50858c2ecf20Sopenharmony_ci
50868c2ecf20Sopenharmony_ci	err = its_init_domain(handle, its);
50878c2ecf20Sopenharmony_ci	if (err)
50888c2ecf20Sopenharmony_ci		goto out_free_tables;
50898c2ecf20Sopenharmony_ci
50908c2ecf20Sopenharmony_ci	raw_spin_lock(&its_lock);
50918c2ecf20Sopenharmony_ci	list_add(&its->entry, &its_nodes);
50928c2ecf20Sopenharmony_ci	raw_spin_unlock(&its_lock);
50938c2ecf20Sopenharmony_ci
50948c2ecf20Sopenharmony_ci	return 0;
50958c2ecf20Sopenharmony_ci
50968c2ecf20Sopenharmony_ciout_free_tables:
50978c2ecf20Sopenharmony_ci	its_free_tables(its);
50988c2ecf20Sopenharmony_ciout_free_cmd:
50998c2ecf20Sopenharmony_ci	free_pages((unsigned long)its->cmd_base, get_order(ITS_CMD_QUEUE_SZ));
51008c2ecf20Sopenharmony_ciout_unmap_sgir:
51018c2ecf20Sopenharmony_ci	if (its->sgir_base)
51028c2ecf20Sopenharmony_ci		iounmap(its->sgir_base);
51038c2ecf20Sopenharmony_ciout_free_its:
51048c2ecf20Sopenharmony_ci	kfree(its);
51058c2ecf20Sopenharmony_ciout_unmap:
51068c2ecf20Sopenharmony_ci	iounmap(its_base);
51078c2ecf20Sopenharmony_ci	pr_err("ITS@%pa: failed probing (%d)\n", &res->start, err);
51088c2ecf20Sopenharmony_ci	return err;
51098c2ecf20Sopenharmony_ci}
51108c2ecf20Sopenharmony_ci
51118c2ecf20Sopenharmony_cistatic bool gic_rdists_supports_plpis(void)
51128c2ecf20Sopenharmony_ci{
51138c2ecf20Sopenharmony_ci	return !!(gic_read_typer(gic_data_rdist_rd_base() + GICR_TYPER) & GICR_TYPER_PLPIS);
51148c2ecf20Sopenharmony_ci}
51158c2ecf20Sopenharmony_ci
51168c2ecf20Sopenharmony_cistatic int redist_disable_lpis(void)
51178c2ecf20Sopenharmony_ci{
51188c2ecf20Sopenharmony_ci	void __iomem *rbase = gic_data_rdist_rd_base();
51198c2ecf20Sopenharmony_ci	u64 timeout = USEC_PER_SEC;
51208c2ecf20Sopenharmony_ci	u64 val;
51218c2ecf20Sopenharmony_ci
51228c2ecf20Sopenharmony_ci	if (!gic_rdists_supports_plpis()) {
51238c2ecf20Sopenharmony_ci		pr_info("CPU%d: LPIs not supported\n", smp_processor_id());
51248c2ecf20Sopenharmony_ci		return -ENXIO;
51258c2ecf20Sopenharmony_ci	}
51268c2ecf20Sopenharmony_ci
51278c2ecf20Sopenharmony_ci	val = readl_relaxed(rbase + GICR_CTLR);
51288c2ecf20Sopenharmony_ci	if (!(val & GICR_CTLR_ENABLE_LPIS))
51298c2ecf20Sopenharmony_ci		return 0;
51308c2ecf20Sopenharmony_ci
51318c2ecf20Sopenharmony_ci	/*
51328c2ecf20Sopenharmony_ci	 * If coming via a CPU hotplug event, we don't need to disable
51338c2ecf20Sopenharmony_ci	 * LPIs before trying to re-enable them. They are already
51348c2ecf20Sopenharmony_ci	 * configured and all is well in the world.
51358c2ecf20Sopenharmony_ci	 *
51368c2ecf20Sopenharmony_ci	 * If running with preallocated tables, there is nothing to do.
51378c2ecf20Sopenharmony_ci	 */
51388c2ecf20Sopenharmony_ci	if (gic_data_rdist()->lpi_enabled ||
51398c2ecf20Sopenharmony_ci	    (gic_rdists->flags & RDIST_FLAGS_RD_TABLES_PREALLOCATED))
51408c2ecf20Sopenharmony_ci		return 0;
51418c2ecf20Sopenharmony_ci
51428c2ecf20Sopenharmony_ci	/*
51438c2ecf20Sopenharmony_ci	 * From that point on, we only try to do some damage control.
51448c2ecf20Sopenharmony_ci	 */
51458c2ecf20Sopenharmony_ci	pr_warn("GICv3: CPU%d: Booted with LPIs enabled, memory probably corrupted\n",
51468c2ecf20Sopenharmony_ci		smp_processor_id());
51478c2ecf20Sopenharmony_ci	add_taint(TAINT_CRAP, LOCKDEP_STILL_OK);
51488c2ecf20Sopenharmony_ci
51498c2ecf20Sopenharmony_ci	/* Disable LPIs */
51508c2ecf20Sopenharmony_ci	val &= ~GICR_CTLR_ENABLE_LPIS;
51518c2ecf20Sopenharmony_ci	writel_relaxed(val, rbase + GICR_CTLR);
51528c2ecf20Sopenharmony_ci
51538c2ecf20Sopenharmony_ci	/* Make sure any change to GICR_CTLR is observable by the GIC */
51548c2ecf20Sopenharmony_ci	dsb(sy);
51558c2ecf20Sopenharmony_ci
51568c2ecf20Sopenharmony_ci	/*
51578c2ecf20Sopenharmony_ci	 * Software must observe RWP==0 after clearing GICR_CTLR.EnableLPIs
51588c2ecf20Sopenharmony_ci	 * from 1 to 0 before programming GICR_PEND{PROP}BASER registers.
51598c2ecf20Sopenharmony_ci	 * Error out if we time out waiting for RWP to clear.
51608c2ecf20Sopenharmony_ci	 */
51618c2ecf20Sopenharmony_ci	while (readl_relaxed(rbase + GICR_CTLR) & GICR_CTLR_RWP) {
51628c2ecf20Sopenharmony_ci		if (!timeout) {
51638c2ecf20Sopenharmony_ci			pr_err("CPU%d: Timeout while disabling LPIs\n",
51648c2ecf20Sopenharmony_ci			       smp_processor_id());
51658c2ecf20Sopenharmony_ci			return -ETIMEDOUT;
51668c2ecf20Sopenharmony_ci		}
51678c2ecf20Sopenharmony_ci		udelay(1);
51688c2ecf20Sopenharmony_ci		timeout--;
51698c2ecf20Sopenharmony_ci	}
51708c2ecf20Sopenharmony_ci
51718c2ecf20Sopenharmony_ci	/*
51728c2ecf20Sopenharmony_ci	 * After it has been written to 1, it is IMPLEMENTATION
51738c2ecf20Sopenharmony_ci	 * DEFINED whether GICR_CTLR.EnableLPI becomes RES1 or can be
51748c2ecf20Sopenharmony_ci	 * cleared to 0. Error out if clearing the bit failed.
51758c2ecf20Sopenharmony_ci	 */
51768c2ecf20Sopenharmony_ci	if (readl_relaxed(rbase + GICR_CTLR) & GICR_CTLR_ENABLE_LPIS) {
51778c2ecf20Sopenharmony_ci		pr_err("CPU%d: Failed to disable LPIs\n", smp_processor_id());
51788c2ecf20Sopenharmony_ci		return -EBUSY;
51798c2ecf20Sopenharmony_ci	}
51808c2ecf20Sopenharmony_ci
51818c2ecf20Sopenharmony_ci	return 0;
51828c2ecf20Sopenharmony_ci}
51838c2ecf20Sopenharmony_ci
51848c2ecf20Sopenharmony_ciint its_cpu_init(void)
51858c2ecf20Sopenharmony_ci{
51868c2ecf20Sopenharmony_ci	if (!list_empty(&its_nodes)) {
51878c2ecf20Sopenharmony_ci		int ret;
51888c2ecf20Sopenharmony_ci
51898c2ecf20Sopenharmony_ci		ret = redist_disable_lpis();
51908c2ecf20Sopenharmony_ci		if (ret)
51918c2ecf20Sopenharmony_ci			return ret;
51928c2ecf20Sopenharmony_ci
51938c2ecf20Sopenharmony_ci		its_cpu_init_lpis();
51948c2ecf20Sopenharmony_ci		its_cpu_init_collections();
51958c2ecf20Sopenharmony_ci	}
51968c2ecf20Sopenharmony_ci
51978c2ecf20Sopenharmony_ci	return 0;
51988c2ecf20Sopenharmony_ci}
51998c2ecf20Sopenharmony_ci
52008c2ecf20Sopenharmony_cistatic const struct of_device_id its_device_id[] = {
52018c2ecf20Sopenharmony_ci	{	.compatible	= "arm,gic-v3-its",	},
52028c2ecf20Sopenharmony_ci	{},
52038c2ecf20Sopenharmony_ci};
52048c2ecf20Sopenharmony_ci
52058c2ecf20Sopenharmony_cistatic int __init its_of_probe(struct device_node *node)
52068c2ecf20Sopenharmony_ci{
52078c2ecf20Sopenharmony_ci	struct device_node *np;
52088c2ecf20Sopenharmony_ci	struct resource res;
52098c2ecf20Sopenharmony_ci
52108c2ecf20Sopenharmony_ci	for (np = of_find_matching_node(node, its_device_id); np;
52118c2ecf20Sopenharmony_ci	     np = of_find_matching_node(np, its_device_id)) {
52128c2ecf20Sopenharmony_ci		if (!of_device_is_available(np))
52138c2ecf20Sopenharmony_ci			continue;
52148c2ecf20Sopenharmony_ci		if (!of_property_read_bool(np, "msi-controller")) {
52158c2ecf20Sopenharmony_ci			pr_warn("%pOF: no msi-controller property, ITS ignored\n",
52168c2ecf20Sopenharmony_ci				np);
52178c2ecf20Sopenharmony_ci			continue;
52188c2ecf20Sopenharmony_ci		}
52198c2ecf20Sopenharmony_ci
52208c2ecf20Sopenharmony_ci		if (of_address_to_resource(np, 0, &res)) {
52218c2ecf20Sopenharmony_ci			pr_warn("%pOF: no regs?\n", np);
52228c2ecf20Sopenharmony_ci			continue;
52238c2ecf20Sopenharmony_ci		}
52248c2ecf20Sopenharmony_ci
52258c2ecf20Sopenharmony_ci		its_probe_one(&res, &np->fwnode, of_node_to_nid(np));
52268c2ecf20Sopenharmony_ci	}
52278c2ecf20Sopenharmony_ci	return 0;
52288c2ecf20Sopenharmony_ci}
52298c2ecf20Sopenharmony_ci
52308c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI
52318c2ecf20Sopenharmony_ci
52328c2ecf20Sopenharmony_ci#define ACPI_GICV3_ITS_MEM_SIZE (SZ_128K)
52338c2ecf20Sopenharmony_ci
52348c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI_NUMA
52358c2ecf20Sopenharmony_cistruct its_srat_map {
52368c2ecf20Sopenharmony_ci	/* numa node id */
52378c2ecf20Sopenharmony_ci	u32	numa_node;
52388c2ecf20Sopenharmony_ci	/* GIC ITS ID */
52398c2ecf20Sopenharmony_ci	u32	its_id;
52408c2ecf20Sopenharmony_ci};
52418c2ecf20Sopenharmony_ci
52428c2ecf20Sopenharmony_cistatic struct its_srat_map *its_srat_maps __initdata;
52438c2ecf20Sopenharmony_cistatic int its_in_srat __initdata;
52448c2ecf20Sopenharmony_ci
52458c2ecf20Sopenharmony_cistatic int __init acpi_get_its_numa_node(u32 its_id)
52468c2ecf20Sopenharmony_ci{
52478c2ecf20Sopenharmony_ci	int i;
52488c2ecf20Sopenharmony_ci
52498c2ecf20Sopenharmony_ci	for (i = 0; i < its_in_srat; i++) {
52508c2ecf20Sopenharmony_ci		if (its_id == its_srat_maps[i].its_id)
52518c2ecf20Sopenharmony_ci			return its_srat_maps[i].numa_node;
52528c2ecf20Sopenharmony_ci	}
52538c2ecf20Sopenharmony_ci	return NUMA_NO_NODE;
52548c2ecf20Sopenharmony_ci}
52558c2ecf20Sopenharmony_ci
52568c2ecf20Sopenharmony_cistatic int __init gic_acpi_match_srat_its(union acpi_subtable_headers *header,
52578c2ecf20Sopenharmony_ci					  const unsigned long end)
52588c2ecf20Sopenharmony_ci{
52598c2ecf20Sopenharmony_ci	return 0;
52608c2ecf20Sopenharmony_ci}
52618c2ecf20Sopenharmony_ci
52628c2ecf20Sopenharmony_cistatic int __init gic_acpi_parse_srat_its(union acpi_subtable_headers *header,
52638c2ecf20Sopenharmony_ci			 const unsigned long end)
52648c2ecf20Sopenharmony_ci{
52658c2ecf20Sopenharmony_ci	int node;
52668c2ecf20Sopenharmony_ci	struct acpi_srat_gic_its_affinity *its_affinity;
52678c2ecf20Sopenharmony_ci
52688c2ecf20Sopenharmony_ci	its_affinity = (struct acpi_srat_gic_its_affinity *)header;
52698c2ecf20Sopenharmony_ci	if (!its_affinity)
52708c2ecf20Sopenharmony_ci		return -EINVAL;
52718c2ecf20Sopenharmony_ci
52728c2ecf20Sopenharmony_ci	if (its_affinity->header.length < sizeof(*its_affinity)) {
52738c2ecf20Sopenharmony_ci		pr_err("SRAT: Invalid header length %d in ITS affinity\n",
52748c2ecf20Sopenharmony_ci			its_affinity->header.length);
52758c2ecf20Sopenharmony_ci		return -EINVAL;
52768c2ecf20Sopenharmony_ci	}
52778c2ecf20Sopenharmony_ci
52788c2ecf20Sopenharmony_ci	/*
52798c2ecf20Sopenharmony_ci	 * Note that in theory a new proximity node could be created by this
52808c2ecf20Sopenharmony_ci	 * entry as it is an SRAT resource allocation structure.
52818c2ecf20Sopenharmony_ci	 * We do not currently support doing so.
52828c2ecf20Sopenharmony_ci	 */
52838c2ecf20Sopenharmony_ci	node = pxm_to_node(its_affinity->proximity_domain);
52848c2ecf20Sopenharmony_ci
52858c2ecf20Sopenharmony_ci	if (node == NUMA_NO_NODE || node >= MAX_NUMNODES) {
52868c2ecf20Sopenharmony_ci		pr_err("SRAT: Invalid NUMA node %d in ITS affinity\n", node);
52878c2ecf20Sopenharmony_ci		return 0;
52888c2ecf20Sopenharmony_ci	}
52898c2ecf20Sopenharmony_ci
52908c2ecf20Sopenharmony_ci	its_srat_maps[its_in_srat].numa_node = node;
52918c2ecf20Sopenharmony_ci	its_srat_maps[its_in_srat].its_id = its_affinity->its_id;
52928c2ecf20Sopenharmony_ci	its_in_srat++;
52938c2ecf20Sopenharmony_ci	pr_info("SRAT: PXM %d -> ITS %d -> Node %d\n",
52948c2ecf20Sopenharmony_ci		its_affinity->proximity_domain, its_affinity->its_id, node);
52958c2ecf20Sopenharmony_ci
52968c2ecf20Sopenharmony_ci	return 0;
52978c2ecf20Sopenharmony_ci}
52988c2ecf20Sopenharmony_ci
52998c2ecf20Sopenharmony_cistatic void __init acpi_table_parse_srat_its(void)
53008c2ecf20Sopenharmony_ci{
53018c2ecf20Sopenharmony_ci	int count;
53028c2ecf20Sopenharmony_ci
53038c2ecf20Sopenharmony_ci	count = acpi_table_parse_entries(ACPI_SIG_SRAT,
53048c2ecf20Sopenharmony_ci			sizeof(struct acpi_table_srat),
53058c2ecf20Sopenharmony_ci			ACPI_SRAT_TYPE_GIC_ITS_AFFINITY,
53068c2ecf20Sopenharmony_ci			gic_acpi_match_srat_its, 0);
53078c2ecf20Sopenharmony_ci	if (count <= 0)
53088c2ecf20Sopenharmony_ci		return;
53098c2ecf20Sopenharmony_ci
53108c2ecf20Sopenharmony_ci	its_srat_maps = kmalloc_array(count, sizeof(struct its_srat_map),
53118c2ecf20Sopenharmony_ci				      GFP_KERNEL);
53128c2ecf20Sopenharmony_ci	if (!its_srat_maps) {
53138c2ecf20Sopenharmony_ci		pr_warn("SRAT: Failed to allocate memory for its_srat_maps!\n");
53148c2ecf20Sopenharmony_ci		return;
53158c2ecf20Sopenharmony_ci	}
53168c2ecf20Sopenharmony_ci
53178c2ecf20Sopenharmony_ci	acpi_table_parse_entries(ACPI_SIG_SRAT,
53188c2ecf20Sopenharmony_ci			sizeof(struct acpi_table_srat),
53198c2ecf20Sopenharmony_ci			ACPI_SRAT_TYPE_GIC_ITS_AFFINITY,
53208c2ecf20Sopenharmony_ci			gic_acpi_parse_srat_its, 0);
53218c2ecf20Sopenharmony_ci}
53228c2ecf20Sopenharmony_ci
53238c2ecf20Sopenharmony_ci/* free the its_srat_maps after ITS probing */
53248c2ecf20Sopenharmony_cistatic void __init acpi_its_srat_maps_free(void)
53258c2ecf20Sopenharmony_ci{
53268c2ecf20Sopenharmony_ci	kfree(its_srat_maps);
53278c2ecf20Sopenharmony_ci}
53288c2ecf20Sopenharmony_ci#else
53298c2ecf20Sopenharmony_cistatic void __init acpi_table_parse_srat_its(void)	{ }
53308c2ecf20Sopenharmony_cistatic int __init acpi_get_its_numa_node(u32 its_id) { return NUMA_NO_NODE; }
53318c2ecf20Sopenharmony_cistatic void __init acpi_its_srat_maps_free(void) { }
53328c2ecf20Sopenharmony_ci#endif
53338c2ecf20Sopenharmony_ci
53348c2ecf20Sopenharmony_cistatic int __init gic_acpi_parse_madt_its(union acpi_subtable_headers *header,
53358c2ecf20Sopenharmony_ci					  const unsigned long end)
53368c2ecf20Sopenharmony_ci{
53378c2ecf20Sopenharmony_ci	struct acpi_madt_generic_translator *its_entry;
53388c2ecf20Sopenharmony_ci	struct fwnode_handle *dom_handle;
53398c2ecf20Sopenharmony_ci	struct resource res;
53408c2ecf20Sopenharmony_ci	int err;
53418c2ecf20Sopenharmony_ci
53428c2ecf20Sopenharmony_ci	its_entry = (struct acpi_madt_generic_translator *)header;
53438c2ecf20Sopenharmony_ci	memset(&res, 0, sizeof(res));
53448c2ecf20Sopenharmony_ci	res.start = its_entry->base_address;
53458c2ecf20Sopenharmony_ci	res.end = its_entry->base_address + ACPI_GICV3_ITS_MEM_SIZE - 1;
53468c2ecf20Sopenharmony_ci	res.flags = IORESOURCE_MEM;
53478c2ecf20Sopenharmony_ci
53488c2ecf20Sopenharmony_ci	dom_handle = irq_domain_alloc_fwnode(&res.start);
53498c2ecf20Sopenharmony_ci	if (!dom_handle) {
53508c2ecf20Sopenharmony_ci		pr_err("ITS@%pa: Unable to allocate GICv3 ITS domain token\n",
53518c2ecf20Sopenharmony_ci		       &res.start);
53528c2ecf20Sopenharmony_ci		return -ENOMEM;
53538c2ecf20Sopenharmony_ci	}
53548c2ecf20Sopenharmony_ci
53558c2ecf20Sopenharmony_ci	err = iort_register_domain_token(its_entry->translation_id, res.start,
53568c2ecf20Sopenharmony_ci					 dom_handle);
53578c2ecf20Sopenharmony_ci	if (err) {
53588c2ecf20Sopenharmony_ci		pr_err("ITS@%pa: Unable to register GICv3 ITS domain token (ITS ID %d) to IORT\n",
53598c2ecf20Sopenharmony_ci		       &res.start, its_entry->translation_id);
53608c2ecf20Sopenharmony_ci		goto dom_err;
53618c2ecf20Sopenharmony_ci	}
53628c2ecf20Sopenharmony_ci
53638c2ecf20Sopenharmony_ci	err = its_probe_one(&res, dom_handle,
53648c2ecf20Sopenharmony_ci			acpi_get_its_numa_node(its_entry->translation_id));
53658c2ecf20Sopenharmony_ci	if (!err)
53668c2ecf20Sopenharmony_ci		return 0;
53678c2ecf20Sopenharmony_ci
53688c2ecf20Sopenharmony_ci	iort_deregister_domain_token(its_entry->translation_id);
53698c2ecf20Sopenharmony_cidom_err:
53708c2ecf20Sopenharmony_ci	irq_domain_free_fwnode(dom_handle);
53718c2ecf20Sopenharmony_ci	return err;
53728c2ecf20Sopenharmony_ci}
53738c2ecf20Sopenharmony_ci
53748c2ecf20Sopenharmony_cistatic void __init its_acpi_probe(void)
53758c2ecf20Sopenharmony_ci{
53768c2ecf20Sopenharmony_ci	acpi_table_parse_srat_its();
53778c2ecf20Sopenharmony_ci	acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_TRANSLATOR,
53788c2ecf20Sopenharmony_ci			      gic_acpi_parse_madt_its, 0);
53798c2ecf20Sopenharmony_ci	acpi_its_srat_maps_free();
53808c2ecf20Sopenharmony_ci}
53818c2ecf20Sopenharmony_ci#else
53828c2ecf20Sopenharmony_cistatic void __init its_acpi_probe(void) { }
53838c2ecf20Sopenharmony_ci#endif
53848c2ecf20Sopenharmony_ci
53858c2ecf20Sopenharmony_ciint __init its_init(struct fwnode_handle *handle, struct rdists *rdists,
53868c2ecf20Sopenharmony_ci		    struct irq_domain *parent_domain)
53878c2ecf20Sopenharmony_ci{
53888c2ecf20Sopenharmony_ci	struct device_node *of_node;
53898c2ecf20Sopenharmony_ci	struct its_node *its;
53908c2ecf20Sopenharmony_ci	bool has_v4 = false;
53918c2ecf20Sopenharmony_ci	bool has_v4_1 = false;
53928c2ecf20Sopenharmony_ci	int err;
53938c2ecf20Sopenharmony_ci
53948c2ecf20Sopenharmony_ci	gic_rdists = rdists;
53958c2ecf20Sopenharmony_ci
53968c2ecf20Sopenharmony_ci	its_parent = parent_domain;
53978c2ecf20Sopenharmony_ci	of_node = to_of_node(handle);
53988c2ecf20Sopenharmony_ci	if (of_node)
53998c2ecf20Sopenharmony_ci		its_of_probe(of_node);
54008c2ecf20Sopenharmony_ci	else
54018c2ecf20Sopenharmony_ci		its_acpi_probe();
54028c2ecf20Sopenharmony_ci
54038c2ecf20Sopenharmony_ci	if (list_empty(&its_nodes)) {
54048c2ecf20Sopenharmony_ci		pr_warn("ITS: No ITS available, not enabling LPIs\n");
54058c2ecf20Sopenharmony_ci		return -ENXIO;
54068c2ecf20Sopenharmony_ci	}
54078c2ecf20Sopenharmony_ci
54088c2ecf20Sopenharmony_ci	err = allocate_lpi_tables();
54098c2ecf20Sopenharmony_ci	if (err)
54108c2ecf20Sopenharmony_ci		return err;
54118c2ecf20Sopenharmony_ci
54128c2ecf20Sopenharmony_ci	list_for_each_entry(its, &its_nodes, entry) {
54138c2ecf20Sopenharmony_ci		has_v4 |= is_v4(its);
54148c2ecf20Sopenharmony_ci		has_v4_1 |= is_v4_1(its);
54158c2ecf20Sopenharmony_ci	}
54168c2ecf20Sopenharmony_ci
54178c2ecf20Sopenharmony_ci	/* Don't bother with inconsistent systems */
54188c2ecf20Sopenharmony_ci	if (WARN_ON(!has_v4_1 && rdists->has_rvpeid))
54198c2ecf20Sopenharmony_ci		rdists->has_rvpeid = false;
54208c2ecf20Sopenharmony_ci
54218c2ecf20Sopenharmony_ci	if (has_v4 & rdists->has_vlpis) {
54228c2ecf20Sopenharmony_ci		const struct irq_domain_ops *sgi_ops;
54238c2ecf20Sopenharmony_ci
54248c2ecf20Sopenharmony_ci		if (has_v4_1)
54258c2ecf20Sopenharmony_ci			sgi_ops = &its_sgi_domain_ops;
54268c2ecf20Sopenharmony_ci		else
54278c2ecf20Sopenharmony_ci			sgi_ops = NULL;
54288c2ecf20Sopenharmony_ci
54298c2ecf20Sopenharmony_ci		if (its_init_vpe_domain() ||
54308c2ecf20Sopenharmony_ci		    its_init_v4(parent_domain, &its_vpe_domain_ops, sgi_ops)) {
54318c2ecf20Sopenharmony_ci			rdists->has_vlpis = false;
54328c2ecf20Sopenharmony_ci			pr_err("ITS: Disabling GICv4 support\n");
54338c2ecf20Sopenharmony_ci		}
54348c2ecf20Sopenharmony_ci	}
54358c2ecf20Sopenharmony_ci
54368c2ecf20Sopenharmony_ci	register_syscore_ops(&its_syscore_ops);
54378c2ecf20Sopenharmony_ci
54388c2ecf20Sopenharmony_ci	return 0;
54398c2ecf20Sopenharmony_ci}
5440