xref: /linux/drivers/irqchip/irq-gic-common.c (revision 4f6b838c378a52ea3ae0b15f12ca8a20849072fa)
1caab277bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2d51d0af4SMarc Zyngier /*
3d51d0af4SMarc Zyngier  * Copyright (C) 2002 ARM Limited, All Rights Reserved.
4d51d0af4SMarc Zyngier  */
5d51d0af4SMarc Zyngier 
6d51d0af4SMarc Zyngier #include <linux/interrupt.h>
7d51d0af4SMarc Zyngier #include <linux/io.h>
8d51d0af4SMarc Zyngier #include <linux/irq.h>
9d51d0af4SMarc Zyngier #include <linux/irqchip/arm-gic.h>
10d51d0af4SMarc Zyngier 
11d51d0af4SMarc Zyngier #include "irq-gic-common.h"
12d51d0af4SMarc Zyngier 
13aa08192aSAniruddha Banerjee static DEFINE_RAW_SPINLOCK(irq_controller_lock);
14aa08192aSAniruddha Banerjee 
15502d6df1SJulien Grall static const struct gic_kvm_info *gic_kvm_info;
16502d6df1SJulien Grall 
17502d6df1SJulien Grall const struct gic_kvm_info *gic_get_kvm_info(void)
18502d6df1SJulien Grall {
19502d6df1SJulien Grall 	return gic_kvm_info;
20502d6df1SJulien Grall }
21502d6df1SJulien Grall 
22502d6df1SJulien Grall void gic_set_kvm_info(const struct gic_kvm_info *info)
23502d6df1SJulien Grall {
24502d6df1SJulien Grall 	BUG_ON(gic_kvm_info != NULL);
25502d6df1SJulien Grall 	gic_kvm_info = info;
26502d6df1SJulien Grall }
27502d6df1SJulien Grall 
28*f70fdb42SSrinivas Kandagatla void gic_enable_of_quirks(const struct device_node *np,
29*f70fdb42SSrinivas Kandagatla 			  const struct gic_quirk *quirks, void *data)
30*f70fdb42SSrinivas Kandagatla {
31*f70fdb42SSrinivas Kandagatla 	for (; quirks->desc; quirks++) {
32*f70fdb42SSrinivas Kandagatla 		if (!of_device_is_compatible(np, quirks->compatible))
33*f70fdb42SSrinivas Kandagatla 			continue;
34*f70fdb42SSrinivas Kandagatla 		if (quirks->init(data))
35*f70fdb42SSrinivas Kandagatla 			pr_info("GIC: enabling workaround for %s\n",
36*f70fdb42SSrinivas Kandagatla 				quirks->desc);
37*f70fdb42SSrinivas Kandagatla 	}
38*f70fdb42SSrinivas Kandagatla }
39*f70fdb42SSrinivas Kandagatla 
4067510ccaSRobert Richter void gic_enable_quirks(u32 iidr, const struct gic_quirk *quirks,
4167510ccaSRobert Richter 		void *data)
4267510ccaSRobert Richter {
4367510ccaSRobert Richter 	for (; quirks->desc; quirks++) {
4425edaed6SMarc Zyngier 		if (quirks->compatible)
4525edaed6SMarc Zyngier 			continue;
4667510ccaSRobert Richter 		if (quirks->iidr != (quirks->mask & iidr))
4767510ccaSRobert Richter 			continue;
489d111d49SArd Biesheuvel 		if (quirks->init(data))
499d111d49SArd Biesheuvel 			pr_info("GIC: enabling workaround for %s\n",
509d111d49SArd Biesheuvel 				quirks->desc);
5167510ccaSRobert Richter 	}
5267510ccaSRobert Richter }
5367510ccaSRobert Richter 
54fb7e7debSLiviu Dudau int gic_configure_irq(unsigned int irq, unsigned int type,
55d51d0af4SMarc Zyngier 		       void __iomem *base, void (*sync_access)(void))
56d51d0af4SMarc Zyngier {
57d51d0af4SMarc Zyngier 	u32 confmask = 0x2 << ((irq % 16) * 2);
58d51d0af4SMarc Zyngier 	u32 confoff = (irq / 16) * 4;
59fb7e7debSLiviu Dudau 	u32 val, oldval;
60fb7e7debSLiviu Dudau 	int ret = 0;
61aa08192aSAniruddha Banerjee 	unsigned long flags;
62d51d0af4SMarc Zyngier 
63d51d0af4SMarc Zyngier 	/*
64d51d0af4SMarc Zyngier 	 * Read current configuration register, and insert the config
65d51d0af4SMarc Zyngier 	 * for "irq", depending on "type".
66d51d0af4SMarc Zyngier 	 */
67aa08192aSAniruddha Banerjee 	raw_spin_lock_irqsave(&irq_controller_lock, flags);
6813d22e2eSMarc Zyngier 	val = oldval = readl_relaxed(base + confoff);
69fb7e7debSLiviu Dudau 	if (type & IRQ_TYPE_LEVEL_MASK)
70d51d0af4SMarc Zyngier 		val &= ~confmask;
71fb7e7debSLiviu Dudau 	else if (type & IRQ_TYPE_EDGE_BOTH)
72d51d0af4SMarc Zyngier 		val |= confmask;
73d51d0af4SMarc Zyngier 
74ec1a454dSJon Hunter 	/* If the current configuration is the same, then we are done */
75aa08192aSAniruddha Banerjee 	if (val == oldval) {
76aa08192aSAniruddha Banerjee 		raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
77ec1a454dSJon Hunter 		return 0;
78aa08192aSAniruddha Banerjee 	}
79ec1a454dSJon Hunter 
80d51d0af4SMarc Zyngier 	/*
81d51d0af4SMarc Zyngier 	 * Write back the new configuration, and possibly re-enable
82992345a5SJon Hunter 	 * the interrupt. If we fail to write a new configuration for
83992345a5SJon Hunter 	 * an SPI then WARN and return an error. If we fail to write the
84992345a5SJon Hunter 	 * configuration for a PPI this is most likely because the GIC
85992345a5SJon Hunter 	 * does not allow us to set the configuration or we are in a
86992345a5SJon Hunter 	 * non-secure mode, and hence it may not be catastrophic.
87d51d0af4SMarc Zyngier 	 */
8813d22e2eSMarc Zyngier 	writel_relaxed(val, base + confoff);
8913d22e2eSMarc Zyngier 	if (readl_relaxed(base + confoff) != val)
90fb7e7debSLiviu Dudau 		ret = -EINVAL;
9113d22e2eSMarc Zyngier 
92aa08192aSAniruddha Banerjee 	raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
93d51d0af4SMarc Zyngier 
94d51d0af4SMarc Zyngier 	if (sync_access)
95d51d0af4SMarc Zyngier 		sync_access();
96fb7e7debSLiviu Dudau 
97fb7e7debSLiviu Dudau 	return ret;
98d51d0af4SMarc Zyngier }
99d51d0af4SMarc Zyngier 
100cdbb813dSJon Hunter void gic_dist_config(void __iomem *base, int gic_irqs,
101d51d0af4SMarc Zyngier 		     void (*sync_access)(void))
102d51d0af4SMarc Zyngier {
103d51d0af4SMarc Zyngier 	unsigned int i;
104d51d0af4SMarc Zyngier 
105d51d0af4SMarc Zyngier 	/*
106d51d0af4SMarc Zyngier 	 * Set all global interrupts to be level triggered, active low.
107d51d0af4SMarc Zyngier 	 */
108d51d0af4SMarc Zyngier 	for (i = 32; i < gic_irqs; i += 16)
109e5f81539SFeng Kan 		writel_relaxed(GICD_INT_ACTLOW_LVLTRIG,
110e5f81539SFeng Kan 					base + GIC_DIST_CONFIG + i / 4);
111d51d0af4SMarc Zyngier 
112d51d0af4SMarc Zyngier 	/*
113d51d0af4SMarc Zyngier 	 * Set priority on all global interrupts.
114d51d0af4SMarc Zyngier 	 */
115d51d0af4SMarc Zyngier 	for (i = 32; i < gic_irqs; i += 4)
116e5f81539SFeng Kan 		writel_relaxed(GICD_INT_DEF_PRI_X4, base + GIC_DIST_PRI + i);
117d51d0af4SMarc Zyngier 
118d51d0af4SMarc Zyngier 	/*
1190eece2b2SMarc Zyngier 	 * Deactivate and disable all SPIs. Leave the PPI and SGIs
1200eece2b2SMarc Zyngier 	 * alone as they are in the redistributor registers on GICv3.
121d51d0af4SMarc Zyngier 	 */
1220eece2b2SMarc Zyngier 	for (i = 32; i < gic_irqs; i += 32) {
1230eece2b2SMarc Zyngier 		writel_relaxed(GICD_INT_EN_CLR_X32,
1240eece2b2SMarc Zyngier 			       base + GIC_DIST_ACTIVE_CLEAR + i / 8);
125e5f81539SFeng Kan 		writel_relaxed(GICD_INT_EN_CLR_X32,
126e5f81539SFeng Kan 			       base + GIC_DIST_ENABLE_CLEAR + i / 8);
1270eece2b2SMarc Zyngier 	}
128d51d0af4SMarc Zyngier 
129d51d0af4SMarc Zyngier 	if (sync_access)
130d51d0af4SMarc Zyngier 		sync_access();
131d51d0af4SMarc Zyngier }
132d51d0af4SMarc Zyngier 
1331a60e1e6SMarc Zyngier void gic_cpu_config(void __iomem *base, int nr, void (*sync_access)(void))
134d51d0af4SMarc Zyngier {
135d51d0af4SMarc Zyngier 	int i;
136d51d0af4SMarc Zyngier 
137d51d0af4SMarc Zyngier 	/*
138d51d0af4SMarc Zyngier 	 * Deal with the banked PPI and SGI interrupts - disable all
1391a60e1e6SMarc Zyngier 	 * private interrupts. Make sure everything is deactivated.
140d51d0af4SMarc Zyngier 	 */
1411a60e1e6SMarc Zyngier 	for (i = 0; i < nr; i += 32) {
1421a60e1e6SMarc Zyngier 		writel_relaxed(GICD_INT_EN_CLR_X32,
1431a60e1e6SMarc Zyngier 			       base + GIC_DIST_ACTIVE_CLEAR + i / 8);
1441a60e1e6SMarc Zyngier 		writel_relaxed(GICD_INT_EN_CLR_X32,
1451a60e1e6SMarc Zyngier 			       base + GIC_DIST_ENABLE_CLEAR + i / 8);
1461a60e1e6SMarc Zyngier 	}
147d51d0af4SMarc Zyngier 
148d51d0af4SMarc Zyngier 	/*
149d51d0af4SMarc Zyngier 	 * Set priority on PPI and SGI interrupts
150d51d0af4SMarc Zyngier 	 */
1511a60e1e6SMarc Zyngier 	for (i = 0; i < nr; i += 4)
152e5f81539SFeng Kan 		writel_relaxed(GICD_INT_DEF_PRI_X4,
153e5f81539SFeng Kan 					base + GIC_DIST_PRI + i * 4 / 4);
154d51d0af4SMarc Zyngier 
155d51d0af4SMarc Zyngier 	if (sync_access)
156d51d0af4SMarc Zyngier 		sync_access();
157d51d0af4SMarc Zyngier }
158