xref: /linux/drivers/irqchip/irq-gic-v4.c (revision 8be98d2f2a0a262f8bf8a0bc1fdf522b3c7aab17)
1caab277bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
27de5c0afSMarc Zyngier /*
37de5c0afSMarc Zyngier  * Copyright (C) 2016,2017 ARM Limited, All Rights Reserved.
47de5c0afSMarc Zyngier  * Author: Marc Zyngier <marc.zyngier@arm.com>
57de5c0afSMarc Zyngier  */
67de5c0afSMarc Zyngier 
77de5c0afSMarc Zyngier #include <linux/interrupt.h>
87de5c0afSMarc Zyngier #include <linux/irq.h>
97de5c0afSMarc Zyngier #include <linux/irqdomain.h>
107de5c0afSMarc Zyngier #include <linux/msi.h>
117de5c0afSMarc Zyngier #include <linux/sched.h>
127de5c0afSMarc Zyngier 
137de5c0afSMarc Zyngier #include <linux/irqchip/arm-gic-v4.h>
147de5c0afSMarc Zyngier 
157954907bSMarc Zyngier /*
167954907bSMarc Zyngier  * WARNING: The blurb below assumes that you understand the
177954907bSMarc Zyngier  * intricacies of GICv3, GICv4, and how a guest's view of a GICv3 gets
187954907bSMarc Zyngier  * translated into GICv4 commands. So it effectively targets at most
197954907bSMarc Zyngier  * two individuals. You know who you are.
207954907bSMarc Zyngier  *
217954907bSMarc Zyngier  * The core GICv4 code is designed to *avoid* exposing too much of the
227954907bSMarc Zyngier  * core GIC code (that would in turn leak into the hypervisor code),
237954907bSMarc Zyngier  * and instead provide a hypervisor agnostic interface to the HW (of
247954907bSMarc Zyngier  * course, the astute reader will quickly realize that hypervisor
257954907bSMarc Zyngier  * agnostic actually means KVM-specific - what were you thinking?).
267954907bSMarc Zyngier  *
277954907bSMarc Zyngier  * In order to achieve a modicum of isolation, we try to hide most of
287954907bSMarc Zyngier  * the GICv4 "stuff" behind normal irqchip operations:
297954907bSMarc Zyngier  *
307954907bSMarc Zyngier  * - Any guest-visible VLPI is backed by a Linux interrupt (and a
317954907bSMarc Zyngier  *   physical LPI which gets unmapped when the guest maps the
327954907bSMarc Zyngier  *   VLPI). This allows the same DevID/EventID pair to be either
337954907bSMarc Zyngier  *   mapped to the LPI (host) or the VLPI (guest). Note that this is
347954907bSMarc Zyngier  *   exclusive, and you cannot have both.
357954907bSMarc Zyngier  *
367954907bSMarc Zyngier  * - Enabling/disabling a VLPI is done by issuing mask/unmask calls.
377954907bSMarc Zyngier  *
387954907bSMarc Zyngier  * - Guest INT/CLEAR commands are implemented through
397954907bSMarc Zyngier  *   irq_set_irqchip_state().
407954907bSMarc Zyngier  *
417954907bSMarc Zyngier  * - The *bizarre* stuff (mapping/unmapping an interrupt to a VLPI, or
427954907bSMarc Zyngier  *   issuing an INV after changing a priority) gets shoved into the
437954907bSMarc Zyngier  *   irq_set_vcpu_affinity() method. While this is quite horrible
447954907bSMarc Zyngier  *   (let's face it, this is the irqchip version of an ioctl), it
457954907bSMarc Zyngier  *   confines the crap to a single location. And map/unmap really is
467954907bSMarc Zyngier  *   about setting the affinity of a VLPI to a vcpu, so only INV is
477954907bSMarc Zyngier  *   majorly out of place. So there.
487954907bSMarc Zyngier  *
497954907bSMarc Zyngier  * A number of commands are simply not provided by this interface, as
507954907bSMarc Zyngier  * they do not make direct sense. For example, MAPD is purely local to
517954907bSMarc Zyngier  * the virtual ITS (because it references a virtual device, and the
527954907bSMarc Zyngier  * physical ITS is still very much in charge of the physical
537954907bSMarc Zyngier  * device). Same goes for things like MAPC (the physical ITS deals
547954907bSMarc Zyngier  * with the actual vPE affinity, and not the braindead concept of
557954907bSMarc Zyngier  * collection). SYNC is not provided either, as each and every command
567954907bSMarc Zyngier  * is followed by a VSYNC. This could be relaxed in the future, should
577954907bSMarc Zyngier  * this be seen as a bottleneck (yes, this means *never*).
587954907bSMarc Zyngier  *
597954907bSMarc Zyngier  * But handling VLPIs is only one side of the job of the GICv4
607954907bSMarc Zyngier  * code. The other (darker) side is to take care of the doorbell
617954907bSMarc Zyngier  * interrupts which are delivered when a VLPI targeting a non-running
627954907bSMarc Zyngier  * vcpu is being made pending.
637954907bSMarc Zyngier  *
647954907bSMarc Zyngier  * The choice made here is that each vcpu (VPE in old northern GICv4
657954907bSMarc Zyngier  * dialect) gets a single doorbell LPI, no matter how many interrupts
667954907bSMarc Zyngier  * are targeting it. This has a nice property, which is that the
677954907bSMarc Zyngier  * interrupt becomes a handle for the VPE, and that the hypervisor
687954907bSMarc Zyngier  * code can manipulate it through the normal interrupt API:
697954907bSMarc Zyngier  *
707954907bSMarc Zyngier  * - VMs (or rather the VM abstraction that matters to the GIC)
717954907bSMarc Zyngier  *   contain an irq domain where each interrupt maps to a VPE. In
727954907bSMarc Zyngier  *   turn, this domain sits on top of the normal LPI allocator, and a
737954907bSMarc Zyngier  *   specially crafted irq_chip implementation.
747954907bSMarc Zyngier  *
757954907bSMarc Zyngier  * - mask/unmask do what is expected on the doorbell interrupt.
767954907bSMarc Zyngier  *
777954907bSMarc Zyngier  * - irq_set_affinity is used to move a VPE from one redistributor to
787954907bSMarc Zyngier  *   another.
797954907bSMarc Zyngier  *
807954907bSMarc Zyngier  * - irq_set_vcpu_affinity once again gets hijacked for the purpose of
817954907bSMarc Zyngier  *   creating a new sub-API, namely scheduling/descheduling a VPE
827954907bSMarc Zyngier  *   (which involves programming GICR_V{PROP,PEND}BASER) and
837954907bSMarc Zyngier  *   performing INVALL operations.
847954907bSMarc Zyngier  */
857954907bSMarc Zyngier 
867de5c0afSMarc Zyngier static struct irq_domain *gic_domain;
877de5c0afSMarc Zyngier static const struct irq_domain_ops *vpe_domain_ops;
88166cba71SMarc Zyngier static const struct irq_domain_ops *sgi_domain_ops;
897de5c0afSMarc Zyngier 
90*46135d6fSLorenzo Pieralisi #ifdef CONFIG_ARM64
91*46135d6fSLorenzo Pieralisi #include <asm/cpufeature.h>
92*46135d6fSLorenzo Pieralisi 
93*46135d6fSLorenzo Pieralisi bool gic_cpuif_has_vsgi(void)
94*46135d6fSLorenzo Pieralisi {
95*46135d6fSLorenzo Pieralisi 	unsigned long fld, reg = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
96*46135d6fSLorenzo Pieralisi 
97*46135d6fSLorenzo Pieralisi 	fld = cpuid_feature_extract_unsigned_field(reg, ID_AA64PFR0_GIC_SHIFT);
98*46135d6fSLorenzo Pieralisi 
99*46135d6fSLorenzo Pieralisi 	return fld >= 0x3;
100*46135d6fSLorenzo Pieralisi }
101*46135d6fSLorenzo Pieralisi #else
102*46135d6fSLorenzo Pieralisi bool gic_cpuif_has_vsgi(void)
103*46135d6fSLorenzo Pieralisi {
104*46135d6fSLorenzo Pieralisi 	return false;
105*46135d6fSLorenzo Pieralisi }
106*46135d6fSLorenzo Pieralisi #endif
107*46135d6fSLorenzo Pieralisi 
108ae699ad3SMarc Zyngier static bool has_v4_1(void)
109ae699ad3SMarc Zyngier {
110ae699ad3SMarc Zyngier 	return !!sgi_domain_ops;
111ae699ad3SMarc Zyngier }
112ae699ad3SMarc Zyngier 
113*46135d6fSLorenzo Pieralisi static bool has_v4_1_sgi(void)
114*46135d6fSLorenzo Pieralisi {
115*46135d6fSLorenzo Pieralisi 	return has_v4_1() && gic_cpuif_has_vsgi();
116*46135d6fSLorenzo Pieralisi }
117*46135d6fSLorenzo Pieralisi 
1186d31b6ffSMarc Zyngier static int its_alloc_vcpu_sgis(struct its_vpe *vpe, int idx)
1196d31b6ffSMarc Zyngier {
1206d31b6ffSMarc Zyngier 	char *name;
1216d31b6ffSMarc Zyngier 	int sgi_base;
1226d31b6ffSMarc Zyngier 
123*46135d6fSLorenzo Pieralisi 	if (!has_v4_1_sgi())
1246d31b6ffSMarc Zyngier 		return 0;
1256d31b6ffSMarc Zyngier 
1266d31b6ffSMarc Zyngier 	name = kasprintf(GFP_KERNEL, "GICv4-sgi-%d", task_pid_nr(current));
1276d31b6ffSMarc Zyngier 	if (!name)
1286d31b6ffSMarc Zyngier 		goto err;
1296d31b6ffSMarc Zyngier 
1306d31b6ffSMarc Zyngier 	vpe->fwnode = irq_domain_alloc_named_id_fwnode(name, idx);
1316d31b6ffSMarc Zyngier 	if (!vpe->fwnode)
1326d31b6ffSMarc Zyngier 		goto err;
1336d31b6ffSMarc Zyngier 
1346d31b6ffSMarc Zyngier 	kfree(name);
1356d31b6ffSMarc Zyngier 	name = NULL;
1366d31b6ffSMarc Zyngier 
1376d31b6ffSMarc Zyngier 	vpe->sgi_domain = irq_domain_create_linear(vpe->fwnode, 16,
1386d31b6ffSMarc Zyngier 						   sgi_domain_ops, vpe);
1396d31b6ffSMarc Zyngier 	if (!vpe->sgi_domain)
1406d31b6ffSMarc Zyngier 		goto err;
1416d31b6ffSMarc Zyngier 
1426d31b6ffSMarc Zyngier 	sgi_base = __irq_domain_alloc_irqs(vpe->sgi_domain, -1, 16,
1436d31b6ffSMarc Zyngier 					       NUMA_NO_NODE, vpe,
1446d31b6ffSMarc Zyngier 					       false, NULL);
1456d31b6ffSMarc Zyngier 	if (sgi_base <= 0)
1466d31b6ffSMarc Zyngier 		goto err;
1476d31b6ffSMarc Zyngier 
1486d31b6ffSMarc Zyngier 	return 0;
1496d31b6ffSMarc Zyngier 
1506d31b6ffSMarc Zyngier err:
1516d31b6ffSMarc Zyngier 	if (vpe->sgi_domain)
1526d31b6ffSMarc Zyngier 		irq_domain_remove(vpe->sgi_domain);
1536d31b6ffSMarc Zyngier 	if (vpe->fwnode)
1546d31b6ffSMarc Zyngier 		irq_domain_free_fwnode(vpe->fwnode);
1556d31b6ffSMarc Zyngier 	kfree(name);
1566d31b6ffSMarc Zyngier 	return -ENOMEM;
1576d31b6ffSMarc Zyngier }
1586d31b6ffSMarc Zyngier 
1597de5c0afSMarc Zyngier int its_alloc_vcpu_irqs(struct its_vm *vm)
1607de5c0afSMarc Zyngier {
1617de5c0afSMarc Zyngier 	int vpe_base_irq, i;
1627de5c0afSMarc Zyngier 
1637de5c0afSMarc Zyngier 	vm->fwnode = irq_domain_alloc_named_id_fwnode("GICv4-vpe",
1647de5c0afSMarc Zyngier 						      task_pid_nr(current));
1657de5c0afSMarc Zyngier 	if (!vm->fwnode)
1667de5c0afSMarc Zyngier 		goto err;
1677de5c0afSMarc Zyngier 
1687de5c0afSMarc Zyngier 	vm->domain = irq_domain_create_hierarchy(gic_domain, 0, vm->nr_vpes,
1697de5c0afSMarc Zyngier 						 vm->fwnode, vpe_domain_ops,
1707de5c0afSMarc Zyngier 						 vm);
1717de5c0afSMarc Zyngier 	if (!vm->domain)
1727de5c0afSMarc Zyngier 		goto err;
1737de5c0afSMarc Zyngier 
1747de5c0afSMarc Zyngier 	for (i = 0; i < vm->nr_vpes; i++) {
1757de5c0afSMarc Zyngier 		vm->vpes[i]->its_vm = vm;
1767de5c0afSMarc Zyngier 		vm->vpes[i]->idai = true;
1777de5c0afSMarc Zyngier 	}
1787de5c0afSMarc Zyngier 
1797de5c0afSMarc Zyngier 	vpe_base_irq = __irq_domain_alloc_irqs(vm->domain, -1, vm->nr_vpes,
1807de5c0afSMarc Zyngier 					       NUMA_NO_NODE, vm,
1817de5c0afSMarc Zyngier 					       false, NULL);
1827de5c0afSMarc Zyngier 	if (vpe_base_irq <= 0)
1837de5c0afSMarc Zyngier 		goto err;
1847de5c0afSMarc Zyngier 
1856d31b6ffSMarc Zyngier 	for (i = 0; i < vm->nr_vpes; i++) {
1866d31b6ffSMarc Zyngier 		int ret;
1877de5c0afSMarc Zyngier 		vm->vpes[i]->irq = vpe_base_irq + i;
1886d31b6ffSMarc Zyngier 		ret = its_alloc_vcpu_sgis(vm->vpes[i], i);
1896d31b6ffSMarc Zyngier 		if (ret)
1906d31b6ffSMarc Zyngier 			goto err;
1916d31b6ffSMarc Zyngier 	}
1927de5c0afSMarc Zyngier 
1937de5c0afSMarc Zyngier 	return 0;
1947de5c0afSMarc Zyngier 
1957de5c0afSMarc Zyngier err:
1967de5c0afSMarc Zyngier 	if (vm->domain)
1977de5c0afSMarc Zyngier 		irq_domain_remove(vm->domain);
1987de5c0afSMarc Zyngier 	if (vm->fwnode)
1997de5c0afSMarc Zyngier 		irq_domain_free_fwnode(vm->fwnode);
2007de5c0afSMarc Zyngier 
2017de5c0afSMarc Zyngier 	return -ENOMEM;
2027de5c0afSMarc Zyngier }
2037de5c0afSMarc Zyngier 
2046d31b6ffSMarc Zyngier static void its_free_sgi_irqs(struct its_vm *vm)
2056d31b6ffSMarc Zyngier {
2066d31b6ffSMarc Zyngier 	int i;
2076d31b6ffSMarc Zyngier 
208*46135d6fSLorenzo Pieralisi 	if (!has_v4_1_sgi())
2096d31b6ffSMarc Zyngier 		return;
2106d31b6ffSMarc Zyngier 
2116d31b6ffSMarc Zyngier 	for (i = 0; i < vm->nr_vpes; i++) {
2126d31b6ffSMarc Zyngier 		unsigned int irq = irq_find_mapping(vm->vpes[i]->sgi_domain, 0);
2136d31b6ffSMarc Zyngier 
2146d31b6ffSMarc Zyngier 		if (WARN_ON(!irq))
2156d31b6ffSMarc Zyngier 			continue;
2166d31b6ffSMarc Zyngier 
2176d31b6ffSMarc Zyngier 		irq_domain_free_irqs(irq, 16);
2186d31b6ffSMarc Zyngier 		irq_domain_remove(vm->vpes[i]->sgi_domain);
2196d31b6ffSMarc Zyngier 		irq_domain_free_fwnode(vm->vpes[i]->fwnode);
2206d31b6ffSMarc Zyngier 	}
2216d31b6ffSMarc Zyngier }
2226d31b6ffSMarc Zyngier 
2237de5c0afSMarc Zyngier void its_free_vcpu_irqs(struct its_vm *vm)
2247de5c0afSMarc Zyngier {
2256d31b6ffSMarc Zyngier 	its_free_sgi_irqs(vm);
2267de5c0afSMarc Zyngier 	irq_domain_free_irqs(vm->vpes[0]->irq, vm->nr_vpes);
2277de5c0afSMarc Zyngier 	irq_domain_remove(vm->domain);
2287de5c0afSMarc Zyngier 	irq_domain_free_fwnode(vm->fwnode);
2297de5c0afSMarc Zyngier }
230eab84318SMarc Zyngier 
231eab84318SMarc Zyngier static int its_send_vpe_cmd(struct its_vpe *vpe, struct its_cmd_info *info)
232eab84318SMarc Zyngier {
233eab84318SMarc Zyngier 	return irq_set_vcpu_affinity(vpe->irq, info);
234eab84318SMarc Zyngier }
235eab84318SMarc Zyngier 
236ae699ad3SMarc Zyngier int its_make_vpe_non_resident(struct its_vpe *vpe, bool db)
237eab84318SMarc Zyngier {
238ae699ad3SMarc Zyngier 	struct irq_desc *desc = irq_to_desc(vpe->irq);
239ae699ad3SMarc Zyngier 	struct its_cmd_info info = { };
2408e01d9a3SMarc Zyngier 	int ret;
241eab84318SMarc Zyngier 
242eab84318SMarc Zyngier 	WARN_ON(preemptible());
243eab84318SMarc Zyngier 
244ae699ad3SMarc Zyngier 	info.cmd_type = DESCHEDULE_VPE;
245ae699ad3SMarc Zyngier 	if (has_v4_1()) {
246ae699ad3SMarc Zyngier 		/* GICv4.1 can directly deal with doorbells */
247ae699ad3SMarc Zyngier 		info.req_db = db;
248ae699ad3SMarc Zyngier 	} else {
249ae699ad3SMarc Zyngier 		/* Undo the nested disable_irq() calls... */
250ae699ad3SMarc Zyngier 		while (db && irqd_irq_disabled(&desc->irq_data))
251ae699ad3SMarc Zyngier 			enable_irq(vpe->irq);
252ae699ad3SMarc Zyngier 	}
253eab84318SMarc Zyngier 
2548e01d9a3SMarc Zyngier 	ret = its_send_vpe_cmd(vpe, &info);
2558e01d9a3SMarc Zyngier 	if (!ret)
256ae699ad3SMarc Zyngier 		vpe->resident = false;
257ae699ad3SMarc Zyngier 
25857e3cebdSShenming Lu 	vpe->ready = false;
25957e3cebdSShenming Lu 
260ae699ad3SMarc Zyngier 	return ret;
261ae699ad3SMarc Zyngier }
262ae699ad3SMarc Zyngier 
263ae699ad3SMarc Zyngier int its_make_vpe_resident(struct its_vpe *vpe, bool g0en, bool g1en)
264ae699ad3SMarc Zyngier {
265ae699ad3SMarc Zyngier 	struct its_cmd_info info = { };
266ae699ad3SMarc Zyngier 	int ret;
267ae699ad3SMarc Zyngier 
268ae699ad3SMarc Zyngier 	WARN_ON(preemptible());
269ae699ad3SMarc Zyngier 
270ae699ad3SMarc Zyngier 	info.cmd_type = SCHEDULE_VPE;
271ae699ad3SMarc Zyngier 	if (has_v4_1()) {
272ae699ad3SMarc Zyngier 		info.g0en = g0en;
273ae699ad3SMarc Zyngier 		info.g1en = g1en;
274ae699ad3SMarc Zyngier 	} else {
275ae699ad3SMarc Zyngier 		/* Disabled the doorbell, as we're about to enter the guest */
276ae699ad3SMarc Zyngier 		disable_irq_nosync(vpe->irq);
277ae699ad3SMarc Zyngier 	}
278ae699ad3SMarc Zyngier 
279ae699ad3SMarc Zyngier 	ret = its_send_vpe_cmd(vpe, &info);
280ae699ad3SMarc Zyngier 	if (!ret)
281ae699ad3SMarc Zyngier 		vpe->resident = true;
2828e01d9a3SMarc Zyngier 
2838e01d9a3SMarc Zyngier 	return ret;
284eab84318SMarc Zyngier }
285eab84318SMarc Zyngier 
28657e3cebdSShenming Lu int its_commit_vpe(struct its_vpe *vpe)
28757e3cebdSShenming Lu {
28857e3cebdSShenming Lu 	struct its_cmd_info info = {
28957e3cebdSShenming Lu 		.cmd_type = COMMIT_VPE,
29057e3cebdSShenming Lu 	};
29157e3cebdSShenming Lu 	int ret;
29257e3cebdSShenming Lu 
29357e3cebdSShenming Lu 	WARN_ON(preemptible());
29457e3cebdSShenming Lu 
29557e3cebdSShenming Lu 	ret = its_send_vpe_cmd(vpe, &info);
29657e3cebdSShenming Lu 	if (!ret)
29757e3cebdSShenming Lu 		vpe->ready = true;
29857e3cebdSShenming Lu 
29957e3cebdSShenming Lu 	return ret;
30057e3cebdSShenming Lu }
30157e3cebdSShenming Lu 
30257e3cebdSShenming Lu 
303eab84318SMarc Zyngier int its_invall_vpe(struct its_vpe *vpe)
304eab84318SMarc Zyngier {
305eab84318SMarc Zyngier 	struct its_cmd_info info = {
306eab84318SMarc Zyngier 		.cmd_type = INVALL_VPE,
307eab84318SMarc Zyngier 	};
308eab84318SMarc Zyngier 
309eab84318SMarc Zyngier 	return its_send_vpe_cmd(vpe, &info);
310eab84318SMarc Zyngier }
311f2eac75dSMarc Zyngier 
312f2eac75dSMarc Zyngier int its_map_vlpi(int irq, struct its_vlpi_map *map)
313f2eac75dSMarc Zyngier {
314f2eac75dSMarc Zyngier 	struct its_cmd_info info = {
315f2eac75dSMarc Zyngier 		.cmd_type = MAP_VLPI,
3166c09ffd0SArnd Bergmann 		{
317f2eac75dSMarc Zyngier 			.map      = map,
3186c09ffd0SArnd Bergmann 		},
319f2eac75dSMarc Zyngier 	};
32090dc7122SMarc Zyngier 	int ret;
321f2eac75dSMarc Zyngier 
322f2eac75dSMarc Zyngier 	/*
323f2eac75dSMarc Zyngier 	 * The host will never see that interrupt firing again, so it
324f2eac75dSMarc Zyngier 	 * is vital that we don't do any lazy masking.
325f2eac75dSMarc Zyngier 	 */
326f2eac75dSMarc Zyngier 	irq_set_status_flags(irq, IRQ_DISABLE_UNLAZY);
327f2eac75dSMarc Zyngier 
32890dc7122SMarc Zyngier 	ret = irq_set_vcpu_affinity(irq, &info);
32990dc7122SMarc Zyngier 	if (ret)
33090dc7122SMarc Zyngier 		irq_clear_status_flags(irq, IRQ_DISABLE_UNLAZY);
33190dc7122SMarc Zyngier 
33290dc7122SMarc Zyngier 	return ret;
333f2eac75dSMarc Zyngier }
334f2eac75dSMarc Zyngier 
335f2eac75dSMarc Zyngier int its_get_vlpi(int irq, struct its_vlpi_map *map)
336f2eac75dSMarc Zyngier {
337f2eac75dSMarc Zyngier 	struct its_cmd_info info = {
338f2eac75dSMarc Zyngier 		.cmd_type = GET_VLPI,
3396c09ffd0SArnd Bergmann 		{
340f2eac75dSMarc Zyngier 			.map      = map,
3416c09ffd0SArnd Bergmann 		},
342f2eac75dSMarc Zyngier 	};
343f2eac75dSMarc Zyngier 
344f2eac75dSMarc Zyngier 	return irq_set_vcpu_affinity(irq, &info);
345f2eac75dSMarc Zyngier }
346f2eac75dSMarc Zyngier 
347f2eac75dSMarc Zyngier int its_unmap_vlpi(int irq)
348f2eac75dSMarc Zyngier {
349f2eac75dSMarc Zyngier 	irq_clear_status_flags(irq, IRQ_DISABLE_UNLAZY);
350f2eac75dSMarc Zyngier 	return irq_set_vcpu_affinity(irq, NULL);
351f2eac75dSMarc Zyngier }
352f2eac75dSMarc Zyngier 
353f2eac75dSMarc Zyngier int its_prop_update_vlpi(int irq, u8 config, bool inv)
354f2eac75dSMarc Zyngier {
355f2eac75dSMarc Zyngier 	struct its_cmd_info info = {
356f2eac75dSMarc Zyngier 		.cmd_type = inv ? PROP_UPDATE_AND_INV_VLPI : PROP_UPDATE_VLPI,
3576c09ffd0SArnd Bergmann 		{
358f2eac75dSMarc Zyngier 			.config   = config,
3596c09ffd0SArnd Bergmann 		},
360f2eac75dSMarc Zyngier 	};
361f2eac75dSMarc Zyngier 
362f2eac75dSMarc Zyngier 	return irq_set_vcpu_affinity(irq, &info);
363f2eac75dSMarc Zyngier }
3643d63cb53SMarc Zyngier 
365d50676f5SMarc Zyngier int its_prop_update_vsgi(int irq, u8 priority, bool group)
366d50676f5SMarc Zyngier {
367d50676f5SMarc Zyngier 	struct its_cmd_info info = {
368d50676f5SMarc Zyngier 		.cmd_type = PROP_UPDATE_VSGI,
369d50676f5SMarc Zyngier 		{
370d50676f5SMarc Zyngier 			.priority	= priority,
371d50676f5SMarc Zyngier 			.group		= group,
372d50676f5SMarc Zyngier 		},
373d50676f5SMarc Zyngier 	};
374d50676f5SMarc Zyngier 
375d50676f5SMarc Zyngier 	return irq_set_vcpu_affinity(irq, &info);
376d50676f5SMarc Zyngier }
377d50676f5SMarc Zyngier 
378166cba71SMarc Zyngier int its_init_v4(struct irq_domain *domain,
379166cba71SMarc Zyngier 		const struct irq_domain_ops *vpe_ops,
380166cba71SMarc Zyngier 		const struct irq_domain_ops *sgi_ops)
3813d63cb53SMarc Zyngier {
3823d63cb53SMarc Zyngier 	if (domain) {
3833d63cb53SMarc Zyngier 		pr_info("ITS: Enabling GICv4 support\n");
3843d63cb53SMarc Zyngier 		gic_domain = domain;
385166cba71SMarc Zyngier 		vpe_domain_ops = vpe_ops;
386166cba71SMarc Zyngier 		sgi_domain_ops = sgi_ops;
3873d63cb53SMarc Zyngier 		return 0;
3883d63cb53SMarc Zyngier 	}
3893d63cb53SMarc Zyngier 
3903d63cb53SMarc Zyngier 	pr_err("ITS: No GICv4 VPE domain allocated\n");
3913d63cb53SMarc Zyngier 	return -ENODEV;
3923d63cb53SMarc Zyngier }
393