xref: /optee_os/core/drivers/gic.c (revision 26ed70ec3afa4776dff3df058723e794e5263a6b)
1b0104773SPascal Brand /*
27315b7b4SJens Wiklander  * Copyright (c) 2016, Linaro Limited
3b0104773SPascal Brand  * Copyright (c) 2014, STMicroelectronics International N.V.
4b0104773SPascal Brand  * All rights reserved.
5b0104773SPascal Brand  *
6b0104773SPascal Brand  * Redistribution and use in source and binary forms, with or without
7b0104773SPascal Brand  * modification, are permitted provided that the following conditions are met:
8b0104773SPascal Brand  *
9b0104773SPascal Brand  * 1. Redistributions of source code must retain the above copyright notice,
10b0104773SPascal Brand  * this list of conditions and the following disclaimer.
11b0104773SPascal Brand  *
12b0104773SPascal Brand  * 2. Redistributions in binary form must reproduce the above copyright notice,
13b0104773SPascal Brand  * this list of conditions and the following disclaimer in the documentation
14b0104773SPascal Brand  * and/or other materials provided with the distribution.
15b0104773SPascal Brand  *
16b0104773SPascal Brand  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17b0104773SPascal Brand  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18b0104773SPascal Brand  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19b0104773SPascal Brand  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20b0104773SPascal Brand  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21b0104773SPascal Brand  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22b0104773SPascal Brand  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23b0104773SPascal Brand  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24b0104773SPascal Brand  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25b0104773SPascal Brand  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26b0104773SPascal Brand  * POSSIBILITY OF SUCH DAMAGE.
27b0104773SPascal Brand  */
28b0104773SPascal Brand 
298ddf5a4eSEtienne Carriere #include <assert.h>
30b0104773SPascal Brand #include <drivers/gic.h>
317315b7b4SJens Wiklander #include <kernel/interrupt.h>
32d13278b8SEtienne Carriere #include <kernel/panic.h>
337315b7b4SJens Wiklander #include <util.h>
34b0104773SPascal Brand #include <io.h>
354de4bebcSJens Wiklander #include <trace.h>
36b0104773SPascal Brand 
37b0104773SPascal Brand /* Offsets from gic.gicc_base */
38b0104773SPascal Brand #define GICC_CTLR		(0x000)
3930a673e3SPeter Maydell #define GICC_PMR		(0x004)
40b0104773SPascal Brand #define GICC_IAR		(0x00C)
41b0104773SPascal Brand #define GICC_EOIR		(0x010)
42b0104773SPascal Brand 
43b0104773SPascal Brand #define GICC_CTLR_ENABLEGRP0	(1 << 0)
44b0104773SPascal Brand #define GICC_CTLR_ENABLEGRP1	(1 << 1)
45b0104773SPascal Brand #define GICC_CTLR_FIQEN		(1 << 3)
46b0104773SPascal Brand 
47b0104773SPascal Brand /* Offsets from gic.gicd_base */
48b0104773SPascal Brand #define GICD_CTLR		(0x000)
49b0104773SPascal Brand #define GICD_TYPER		(0x004)
50b0104773SPascal Brand #define GICD_IGROUPR(n)		(0x080 + (n) * 4)
51b0104773SPascal Brand #define GICD_ISENABLER(n)	(0x100 + (n) * 4)
52b0104773SPascal Brand #define GICD_ICENABLER(n)	(0x180 + (n) * 4)
53*26ed70ecSGuanchao Liang #define GICD_ISPENDR(n)		(0x200 + (n) * 4)
54b0104773SPascal Brand #define GICD_ICPENDR(n)		(0x280 + (n) * 4)
55b0104773SPascal Brand #define GICD_IPRIORITYR(n)	(0x400 + (n) * 4)
56b0104773SPascal Brand #define GICD_ITARGETSR(n)	(0x800 + (n) * 4)
57*26ed70ecSGuanchao Liang #define GICD_SGIR		(0xF00)
58b0104773SPascal Brand 
59b0104773SPascal Brand #define GICD_CTLR_ENABLEGRP0	(1 << 0)
60b0104773SPascal Brand #define GICD_CTLR_ENABLEGRP1	(1 << 1)
61b0104773SPascal Brand 
6253bd332aSSY Chiu /* Number of Private Peripheral Interrupt */
6353bd332aSSY Chiu #define NUM_PPI	32
6453bd332aSSY Chiu 
65*26ed70ecSGuanchao Liang /* Number of Software Generated Interrupt */
66*26ed70ecSGuanchao Liang #define NUM_SGI			16
67*26ed70ecSGuanchao Liang 
68*26ed70ecSGuanchao Liang /* Number of Non-secure Software Generated Interrupt */
69*26ed70ecSGuanchao Liang #define NUM_NS_SGI		8
70*26ed70ecSGuanchao Liang 
7153bd332aSSY Chiu /* Number of interrupts in one register */
7253bd332aSSY Chiu #define NUM_INTS_PER_REG	32
7353bd332aSSY Chiu 
7453bd332aSSY Chiu /* Number of targets in one register */
7553bd332aSSY Chiu #define NUM_TARGETS_PER_REG	4
7653bd332aSSY Chiu 
7753bd332aSSY Chiu /* Accessors to access ITARGETSRn */
7853bd332aSSY Chiu #define ITARGETSR_FIELD_BITS	8
7953bd332aSSY Chiu #define ITARGETSR_FIELD_MASK	0xff
8053bd332aSSY Chiu 
81b0104773SPascal Brand /* Maximum number of interrups a GIC can support */
82b0104773SPascal Brand #define GIC_MAX_INTS		1020
83b0104773SPascal Brand 
847315b7b4SJens Wiklander #define GIC_SPURIOUS_ID		1023
85b0104773SPascal Brand 
867315b7b4SJens Wiklander #define GICC_IAR_IT_ID_MASK	0x3ff
877315b7b4SJens Wiklander #define GICC_IAR_CPU_ID_MASK	0x7
887315b7b4SJens Wiklander #define GICC_IAR_CPU_ID_SHIFT	10
89b0104773SPascal Brand 
907315b7b4SJens Wiklander static void gic_op_add(struct itr_chip *chip, size_t it, uint32_t flags);
917315b7b4SJens Wiklander static void gic_op_enable(struct itr_chip *chip, size_t it);
927315b7b4SJens Wiklander static void gic_op_disable(struct itr_chip *chip, size_t it);
93*26ed70ecSGuanchao Liang static void gic_op_raise_pi(struct itr_chip *chip, size_t it);
94*26ed70ecSGuanchao Liang static void gic_op_raise_sgi(struct itr_chip *chip, size_t it,
95*26ed70ecSGuanchao Liang 			uint8_t cpu_mask);
96*26ed70ecSGuanchao Liang static void gic_op_set_affinity(struct itr_chip *chip, size_t it,
97*26ed70ecSGuanchao Liang 			uint8_t cpu_mask);
987315b7b4SJens Wiklander 
997315b7b4SJens Wiklander static const struct itr_ops gic_ops = {
1007315b7b4SJens Wiklander 	.add = gic_op_add,
1017315b7b4SJens Wiklander 	.enable = gic_op_enable,
1027315b7b4SJens Wiklander 	.disable = gic_op_disable,
103*26ed70ecSGuanchao Liang 	.raise_pi = gic_op_raise_pi,
104*26ed70ecSGuanchao Liang 	.raise_sgi = gic_op_raise_sgi,
105*26ed70ecSGuanchao Liang 	.set_affinity = gic_op_set_affinity,
1067315b7b4SJens Wiklander };
1077315b7b4SJens Wiklander 
1087315b7b4SJens Wiklander static size_t probe_max_it(vaddr_t gicc_base, vaddr_t gicd_base)
109b0104773SPascal Brand {
110b0104773SPascal Brand 	int i;
111b0104773SPascal Brand 	uint32_t old_ctlr;
112b0104773SPascal Brand 	size_t ret = 0;
11379f008d3SJens Wiklander 	const size_t max_regs = ((GIC_MAX_INTS + NUM_INTS_PER_REG - 1) /
11479f008d3SJens Wiklander 					NUM_INTS_PER_REG) - 1;
115b0104773SPascal Brand 
116b0104773SPascal Brand 	/*
117b0104773SPascal Brand 	 * Probe which interrupt number is the largest.
118b0104773SPascal Brand 	 */
1197315b7b4SJens Wiklander 	old_ctlr = read32(gicc_base + GICC_CTLR);
1207315b7b4SJens Wiklander 	write32(0, gicc_base + GICC_CTLR);
12179f008d3SJens Wiklander 	for (i = max_regs; i >= 0; i--) {
122b0104773SPascal Brand 		uint32_t old_reg;
123b0104773SPascal Brand 		uint32_t reg;
124b0104773SPascal Brand 		int b;
125b0104773SPascal Brand 
1267315b7b4SJens Wiklander 		old_reg = read32(gicd_base + GICD_ISENABLER(i));
1277315b7b4SJens Wiklander 		write32(0xffffffff, gicd_base + GICD_ISENABLER(i));
1287315b7b4SJens Wiklander 		reg = read32(gicd_base + GICD_ISENABLER(i));
1297315b7b4SJens Wiklander 		write32(old_reg, gicd_base + GICD_ICENABLER(i));
13079f008d3SJens Wiklander 		for (b = NUM_INTS_PER_REG - 1; b >= 0; b--) {
131007a97a2SJens Wiklander 			if (BIT32(b) & reg) {
13253bd332aSSY Chiu 				ret = i * NUM_INTS_PER_REG + b;
133b0104773SPascal Brand 				goto out;
134b0104773SPascal Brand 			}
135b0104773SPascal Brand 		}
136b0104773SPascal Brand 	}
137b0104773SPascal Brand out:
1387315b7b4SJens Wiklander 	write32(old_ctlr, gicc_base + GICC_CTLR);
139b0104773SPascal Brand 	return ret;
140b0104773SPascal Brand }
141b0104773SPascal Brand 
1427315b7b4SJens Wiklander void gic_cpu_init(struct gic_data *gd)
143bedc2b9fSsunny {
14405efe1e1SEtienne Carriere 	assert(gd->gicd_base && gd->gicc_base);
14505efe1e1SEtienne Carriere 
146e06e6e74SPeter Maydell 	/* per-CPU interrupts config:
147bedc2b9fSsunny 	 * ID0-ID7(SGI)   for Non-secure interrupts
148bedc2b9fSsunny 	 * ID8-ID15(SGI)  for Secure interrupts.
149bedc2b9fSsunny 	 * All PPI config as Non-secure interrupts.
150bedc2b9fSsunny 	 */
1517315b7b4SJens Wiklander 	write32(0xffff00ff, gd->gicd_base + GICD_IGROUPR(0));
152bedc2b9fSsunny 
15330a673e3SPeter Maydell 	/* Set the priority mask to permit Non-secure interrupts, and to
15430a673e3SPeter Maydell 	 * allow the Non-secure world to adjust the priority mask itself
15530a673e3SPeter Maydell 	 */
1567315b7b4SJens Wiklander 	write32(0x80, gd->gicc_base + GICC_PMR);
15730a673e3SPeter Maydell 
158bedc2b9fSsunny 	/* Enable GIC */
159bedc2b9fSsunny 	write32(GICC_CTLR_ENABLEGRP0 | GICC_CTLR_ENABLEGRP1 | GICC_CTLR_FIQEN,
1607315b7b4SJens Wiklander 		gd->gicc_base + GICC_CTLR);
161bedc2b9fSsunny }
162bedc2b9fSsunny 
1637315b7b4SJens Wiklander void gic_init(struct gic_data *gd, vaddr_t gicc_base, vaddr_t gicd_base)
164b0104773SPascal Brand {
165b0104773SPascal Brand 	size_t n;
166b0104773SPascal Brand 
1677315b7b4SJens Wiklander 	gic_init_base_addr(gd, gicc_base, gicd_base);
168b0104773SPascal Brand 
1697315b7b4SJens Wiklander 	for (n = 0; n <= gd->max_it / NUM_INTS_PER_REG; n++) {
170b0104773SPascal Brand 		/* Disable interrupts */
1717315b7b4SJens Wiklander 		write32(0xffffffff, gd->gicd_base + GICD_ICENABLER(n));
172b0104773SPascal Brand 
173b0104773SPascal Brand 		/* Make interrupts non-pending */
1747315b7b4SJens Wiklander 		write32(0xffffffff, gd->gicd_base + GICD_ICPENDR(n));
175b0104773SPascal Brand 
176b0104773SPascal Brand 		/* Mark interrupts non-secure */
177bedc2b9fSsunny 		if (n == 0) {
178bedc2b9fSsunny 			/* per-CPU inerrupts config:
179bedc2b9fSsunny                          * ID0-ID7(SGI)   for Non-secure interrupts
180bedc2b9fSsunny                          * ID8-ID15(SGI)  for Secure interrupts.
181bedc2b9fSsunny                          * All PPI config as Non-secure interrupts.
182bedc2b9fSsunny 			 */
1837315b7b4SJens Wiklander 			write32(0xffff00ff, gd->gicd_base + GICD_IGROUPR(n));
184bedc2b9fSsunny 		} else {
1857315b7b4SJens Wiklander 			write32(0xffffffff, gd->gicd_base + GICD_IGROUPR(n));
186b0104773SPascal Brand 		}
187bedc2b9fSsunny 	}
188b0104773SPascal Brand 
18930a673e3SPeter Maydell 	/* Set the priority mask to permit Non-secure interrupts, and to
19030a673e3SPeter Maydell 	 * allow the Non-secure world to adjust the priority mask itself
19130a673e3SPeter Maydell 	 */
1927315b7b4SJens Wiklander 	write32(0x80, gd->gicc_base + GICC_PMR);
19330a673e3SPeter Maydell 
194b0104773SPascal Brand 	/* Enable GIC */
195b0104773SPascal Brand 	write32(GICC_CTLR_ENABLEGRP0 | GICC_CTLR_ENABLEGRP1 | GICC_CTLR_FIQEN,
1967315b7b4SJens Wiklander 		gd->gicc_base + GICC_CTLR);
197b0104773SPascal Brand 	write32(GICD_CTLR_ENABLEGRP0 | GICD_CTLR_ENABLEGRP1,
1987315b7b4SJens Wiklander 		gd->gicd_base + GICD_CTLR);
199b0104773SPascal Brand }
200b0104773SPascal Brand 
2017315b7b4SJens Wiklander void gic_init_base_addr(struct gic_data *gd, vaddr_t gicc_base,
2027315b7b4SJens Wiklander 			vaddr_t gicd_base)
20353bd332aSSY Chiu {
2047315b7b4SJens Wiklander 	gd->gicc_base = gicc_base;
2057315b7b4SJens Wiklander 	gd->gicd_base = gicd_base;
2067315b7b4SJens Wiklander 	gd->max_it = probe_max_it(gicc_base, gicd_base);
2077315b7b4SJens Wiklander 	gd->chip.ops = &gic_ops;
20853bd332aSSY Chiu }
20953bd332aSSY Chiu 
2107315b7b4SJens Wiklander static void gic_it_add(struct gic_data *gd, size_t it)
211b0104773SPascal Brand {
21253bd332aSSY Chiu 	size_t idx = it / NUM_INTS_PER_REG;
21353bd332aSSY Chiu 	uint32_t mask = 1 << (it % NUM_INTS_PER_REG);
214b0104773SPascal Brand 
215b0104773SPascal Brand 	/* Disable the interrupt */
2167315b7b4SJens Wiklander 	write32(mask, gd->gicd_base + GICD_ICENABLER(idx));
217b0104773SPascal Brand 	/* Make it non-pending */
2187315b7b4SJens Wiklander 	write32(mask, gd->gicd_base + GICD_ICPENDR(idx));
219b0104773SPascal Brand 	/* Assign it to group0 */
2207315b7b4SJens Wiklander 	write32(read32(gd->gicd_base + GICD_IGROUPR(idx)) & ~mask,
2217315b7b4SJens Wiklander 			gd->gicd_base + GICD_IGROUPR(idx));
222b0104773SPascal Brand }
223b0104773SPascal Brand 
2247315b7b4SJens Wiklander static void gic_it_set_cpu_mask(struct gic_data *gd, size_t it,
2257315b7b4SJens Wiklander 				uint8_t cpu_mask)
226b0104773SPascal Brand {
2278ddf5a4eSEtienne Carriere 	size_t idx __maybe_unused = it / NUM_INTS_PER_REG;
2288ddf5a4eSEtienne Carriere 	uint32_t mask __maybe_unused = 1 << (it % NUM_INTS_PER_REG);
22953bd332aSSY Chiu 	uint32_t target, target_shift;
230b0104773SPascal Brand 
231b0104773SPascal Brand 	/* Assigned to group0 */
2327315b7b4SJens Wiklander 	assert(!(read32(gd->gicd_base + GICD_IGROUPR(idx)) & mask));
233b0104773SPascal Brand 
234b0104773SPascal Brand 	/* Route it to selected CPUs */
2357315b7b4SJens Wiklander 	target = read32(gd->gicd_base +
2367315b7b4SJens Wiklander 			GICD_ITARGETSR(it / NUM_TARGETS_PER_REG));
23753bd332aSSY Chiu 	target_shift = (it % NUM_TARGETS_PER_REG) * ITARGETSR_FIELD_BITS;
23853bd332aSSY Chiu 	target &= ~(ITARGETSR_FIELD_MASK << target_shift);
23953bd332aSSY Chiu 	target |= cpu_mask << target_shift;
2401f60363aSJens Wiklander 	DMSG("cpu_mask: writing 0x%x to 0x%" PRIxVA,
2417315b7b4SJens Wiklander 	     target, gd->gicd_base + GICD_ITARGETSR(it / NUM_TARGETS_PER_REG));
2427315b7b4SJens Wiklander 	write32(target,
2437315b7b4SJens Wiklander 		gd->gicd_base + GICD_ITARGETSR(it / NUM_TARGETS_PER_REG));
244b0104773SPascal Brand 	DMSG("cpu_mask: 0x%x\n",
2457315b7b4SJens Wiklander 	     read32(gd->gicd_base + GICD_ITARGETSR(it / NUM_TARGETS_PER_REG)));
246b0104773SPascal Brand }
247b0104773SPascal Brand 
2487315b7b4SJens Wiklander static void gic_it_set_prio(struct gic_data *gd, size_t it, uint8_t prio)
249b0104773SPascal Brand {
2508ddf5a4eSEtienne Carriere 	size_t idx __maybe_unused = it / NUM_INTS_PER_REG;
2518ddf5a4eSEtienne Carriere 	uint32_t mask __maybe_unused = 1 << (it % NUM_INTS_PER_REG);
252b0104773SPascal Brand 
253b0104773SPascal Brand 	/* Assigned to group0 */
2547315b7b4SJens Wiklander 	assert(!(read32(gd->gicd_base + GICD_IGROUPR(idx)) & mask));
255b0104773SPascal Brand 
256b0104773SPascal Brand 	/* Set prio it to selected CPUs */
2571f60363aSJens Wiklander 	DMSG("prio: writing 0x%x to 0x%" PRIxVA,
2587315b7b4SJens Wiklander 		prio, gd->gicd_base + GICD_IPRIORITYR(0) + it);
2597315b7b4SJens Wiklander 	write8(prio, gd->gicd_base + GICD_IPRIORITYR(0) + it);
260b0104773SPascal Brand }
261b0104773SPascal Brand 
2627315b7b4SJens Wiklander static void gic_it_enable(struct gic_data *gd, size_t it)
263b0104773SPascal Brand {
26453bd332aSSY Chiu 	size_t idx = it / NUM_INTS_PER_REG;
26553bd332aSSY Chiu 	uint32_t mask = 1 << (it % NUM_INTS_PER_REG);
266b0104773SPascal Brand 
267b0104773SPascal Brand 	/* Assigned to group0 */
2687315b7b4SJens Wiklander 	assert(!(read32(gd->gicd_base + GICD_IGROUPR(idx)) & mask));
269*26ed70ecSGuanchao Liang 	if (it >= NUM_SGI) {
270*26ed70ecSGuanchao Liang 		/*
271*26ed70ecSGuanchao Liang 		 * Not enabled yet, except Software Generated Interrupt
272*26ed70ecSGuanchao Liang 		 * which is implementation defined
273*26ed70ecSGuanchao Liang 		 */
2747315b7b4SJens Wiklander 		assert(!(read32(gd->gicd_base + GICD_ISENABLER(idx)) & mask));
275*26ed70ecSGuanchao Liang 	}
276b0104773SPascal Brand 
277b0104773SPascal Brand 	/* Enable the interrupt */
2787315b7b4SJens Wiklander 	write32(mask, gd->gicd_base + GICD_ISENABLER(idx));
279b0104773SPascal Brand }
280b0104773SPascal Brand 
2817315b7b4SJens Wiklander static void gic_it_disable(struct gic_data *gd, size_t it)
282b0104773SPascal Brand {
28353bd332aSSY Chiu 	size_t idx = it / NUM_INTS_PER_REG;
28453bd332aSSY Chiu 	uint32_t mask = 1 << (it % NUM_INTS_PER_REG);
285b0104773SPascal Brand 
286b0104773SPascal Brand 	/* Assigned to group0 */
2877315b7b4SJens Wiklander 	assert(!(read32(gd->gicd_base + GICD_IGROUPR(idx)) & mask));
288b0104773SPascal Brand 
289b0104773SPascal Brand 	/* Disable the interrupt */
2907315b7b4SJens Wiklander 	write32(mask, gd->gicd_base + GICD_ICENABLER(idx));
291b0104773SPascal Brand }
292b0104773SPascal Brand 
293*26ed70ecSGuanchao Liang static void gic_it_set_pending(struct gic_data *gd, size_t it)
294*26ed70ecSGuanchao Liang {
295*26ed70ecSGuanchao Liang 	size_t idx = it / NUM_INTS_PER_REG;
296*26ed70ecSGuanchao Liang 	uint32_t mask = BIT32(it % NUM_INTS_PER_REG);
297*26ed70ecSGuanchao Liang 
298*26ed70ecSGuanchao Liang 	/* Should be Peripheral Interrupt */
299*26ed70ecSGuanchao Liang 	assert(it >= NUM_SGI);
300*26ed70ecSGuanchao Liang 	/* Assigned to group0 */
301*26ed70ecSGuanchao Liang 	assert(!(read32(gd->gicd_base + GICD_IGROUPR(idx)) & mask));
302*26ed70ecSGuanchao Liang 
303*26ed70ecSGuanchao Liang 	/* Raise the interrupt */
304*26ed70ecSGuanchao Liang 	write32(mask, gd->gicd_base + GICD_ISPENDR(idx));
305*26ed70ecSGuanchao Liang }
306*26ed70ecSGuanchao Liang 
307*26ed70ecSGuanchao Liang static void gic_it_raise_sgi(struct gic_data *gd, size_t it,
308*26ed70ecSGuanchao Liang 		uint8_t cpu_mask, uint8_t group)
309*26ed70ecSGuanchao Liang {
310*26ed70ecSGuanchao Liang 	uint32_t mask_id = it & 0xf;
311*26ed70ecSGuanchao Liang 	uint32_t mask_group = group & 0x1;
312*26ed70ecSGuanchao Liang 	uint32_t mask_cpu = cpu_mask & 0xff;
313*26ed70ecSGuanchao Liang 	uint32_t mask = (mask_id | SHIFT_U32(mask_group, 15) |
314*26ed70ecSGuanchao Liang 		SHIFT_U32(mask_cpu, 16));
315*26ed70ecSGuanchao Liang 
316*26ed70ecSGuanchao Liang 	/* Should be Software Generated Interrupt */
317*26ed70ecSGuanchao Liang 	assert(it < NUM_SGI);
318*26ed70ecSGuanchao Liang 
319*26ed70ecSGuanchao Liang 	/* Raise the interrupt */
320*26ed70ecSGuanchao Liang 	write32(mask, gd->gicd_base + GICD_SGIR);
321*26ed70ecSGuanchao Liang }
322*26ed70ecSGuanchao Liang 
3237315b7b4SJens Wiklander static uint32_t gic_read_iar(struct gic_data *gd)
324b0104773SPascal Brand {
3257315b7b4SJens Wiklander 	return read32(gd->gicc_base + GICC_IAR);
326b0104773SPascal Brand }
327b0104773SPascal Brand 
3287315b7b4SJens Wiklander static void gic_write_eoir(struct gic_data *gd, uint32_t eoir)
329b0104773SPascal Brand {
3307315b7b4SJens Wiklander 	write32(eoir, gd->gicc_base + GICC_EOIR);
331b0104773SPascal Brand }
332b0104773SPascal Brand 
3337315b7b4SJens Wiklander static bool gic_it_is_enabled(struct gic_data *gd, size_t it)
3347315b7b4SJens Wiklander {
33553bd332aSSY Chiu 	size_t idx = it / NUM_INTS_PER_REG;
33653bd332aSSY Chiu 	uint32_t mask = 1 << (it % NUM_INTS_PER_REG);
3377315b7b4SJens Wiklander 	return !!(read32(gd->gicd_base + GICD_ISENABLER(idx)) & mask);
33853bd332aSSY Chiu }
33953bd332aSSY Chiu 
3407315b7b4SJens Wiklander static bool __maybe_unused gic_it_get_group(struct gic_data *gd, size_t it)
3417315b7b4SJens Wiklander {
34253bd332aSSY Chiu 	size_t idx = it / NUM_INTS_PER_REG;
34353bd332aSSY Chiu 	uint32_t mask = 1 << (it % NUM_INTS_PER_REG);
3447315b7b4SJens Wiklander 	return !!(read32(gd->gicd_base + GICD_IGROUPR(idx)) & mask);
34553bd332aSSY Chiu }
34653bd332aSSY Chiu 
3477315b7b4SJens Wiklander static uint32_t __maybe_unused gic_it_get_target(struct gic_data *gd, size_t it)
3487315b7b4SJens Wiklander {
34953bd332aSSY Chiu 	size_t reg_idx = it / NUM_TARGETS_PER_REG;
3507315b7b4SJens Wiklander 	uint32_t target_shift = (it % NUM_TARGETS_PER_REG) *
3517315b7b4SJens Wiklander 				ITARGETSR_FIELD_BITS;
35253bd332aSSY Chiu 	uint32_t target_mask = ITARGETSR_FIELD_MASK << target_shift;
35353bd332aSSY Chiu 	uint32_t target =
3547315b7b4SJens Wiklander 		read32(gd->gicd_base + GICD_ITARGETSR(reg_idx)) & target_mask;
3557315b7b4SJens Wiklander 
35653bd332aSSY Chiu 	target = target >> target_shift;
35753bd332aSSY Chiu 	return target;
35853bd332aSSY Chiu }
35953bd332aSSY Chiu 
3607315b7b4SJens Wiklander void gic_dump_state(struct gic_data *gd)
36153bd332aSSY Chiu {
36253bd332aSSY Chiu 	int i;
36353bd332aSSY Chiu 
3647315b7b4SJens Wiklander 	DMSG("GICC_CTLR: 0x%x", read32(gd->gicc_base + GICC_CTLR));
3657315b7b4SJens Wiklander 	DMSG("GICD_CTLR: 0x%x", read32(gd->gicd_base + GICD_CTLR));
3667315b7b4SJens Wiklander 
3677315b7b4SJens Wiklander 	for (i = 0; i < (int)gd->max_it; i++) {
3687315b7b4SJens Wiklander 		if (gic_it_is_enabled(gd, i)) {
36953bd332aSSY Chiu 			DMSG("irq%d: enabled, group:%d, target:%x", i,
3707315b7b4SJens Wiklander 			     gic_it_get_group(gd, i), gic_it_get_target(gd, i));
37153bd332aSSY Chiu 		}
37253bd332aSSY Chiu 	}
37353bd332aSSY Chiu }
3747315b7b4SJens Wiklander 
3757315b7b4SJens Wiklander void gic_it_handle(struct gic_data *gd)
3767315b7b4SJens Wiklander {
3777315b7b4SJens Wiklander 	uint32_t iar;
3787315b7b4SJens Wiklander 	uint32_t id;
3797315b7b4SJens Wiklander 
3807315b7b4SJens Wiklander 	iar = gic_read_iar(gd);
3817315b7b4SJens Wiklander 	id = iar & GICC_IAR_IT_ID_MASK;
3827315b7b4SJens Wiklander 
3837315b7b4SJens Wiklander 	if (id == GIC_SPURIOUS_ID)
3847315b7b4SJens Wiklander 		DMSG("ignoring spurious interrupt");
3857315b7b4SJens Wiklander 	else
3867315b7b4SJens Wiklander 		itr_handle(id);
3877315b7b4SJens Wiklander 
3887315b7b4SJens Wiklander 	gic_write_eoir(gd, iar);
3897315b7b4SJens Wiklander }
3907315b7b4SJens Wiklander 
3917315b7b4SJens Wiklander static void gic_op_add(struct itr_chip *chip, size_t it,
3927315b7b4SJens Wiklander 		       uint32_t flags __unused)
3937315b7b4SJens Wiklander {
3947315b7b4SJens Wiklander 	struct gic_data *gd = container_of(chip, struct gic_data, chip);
3957315b7b4SJens Wiklander 
396d13278b8SEtienne Carriere 	if (it >= gd->max_it)
397d13278b8SEtienne Carriere 		panic();
398d13278b8SEtienne Carriere 
3997315b7b4SJens Wiklander 	gic_it_add(gd, it);
4007315b7b4SJens Wiklander 	/* Set the CPU mask to deliver interrupts to any online core */
4017315b7b4SJens Wiklander 	gic_it_set_cpu_mask(gd, it, 0xff);
4027315b7b4SJens Wiklander 	gic_it_set_prio(gd, it, 0x1);
4037315b7b4SJens Wiklander }
4047315b7b4SJens Wiklander 
4057315b7b4SJens Wiklander static void gic_op_enable(struct itr_chip *chip, size_t it)
4067315b7b4SJens Wiklander {
4077315b7b4SJens Wiklander 	struct gic_data *gd = container_of(chip, struct gic_data, chip);
4087315b7b4SJens Wiklander 
409d13278b8SEtienne Carriere 	if (it >= gd->max_it)
410d13278b8SEtienne Carriere 		panic();
411d13278b8SEtienne Carriere 
4127315b7b4SJens Wiklander 	gic_it_enable(gd, it);
4137315b7b4SJens Wiklander }
4147315b7b4SJens Wiklander 
4157315b7b4SJens Wiklander static void gic_op_disable(struct itr_chip *chip, size_t it)
4167315b7b4SJens Wiklander {
4177315b7b4SJens Wiklander 	struct gic_data *gd = container_of(chip, struct gic_data, chip);
4187315b7b4SJens Wiklander 
419d13278b8SEtienne Carriere 	if (it >= gd->max_it)
420d13278b8SEtienne Carriere 		panic();
421d13278b8SEtienne Carriere 
4227315b7b4SJens Wiklander 	gic_it_disable(gd, it);
4237315b7b4SJens Wiklander }
424*26ed70ecSGuanchao Liang 
425*26ed70ecSGuanchao Liang static void gic_op_raise_pi(struct itr_chip *chip, size_t it)
426*26ed70ecSGuanchao Liang {
427*26ed70ecSGuanchao Liang 	struct gic_data *gd = container_of(chip, struct gic_data, chip);
428*26ed70ecSGuanchao Liang 
429*26ed70ecSGuanchao Liang 	if (it >= gd->max_it)
430*26ed70ecSGuanchao Liang 		panic();
431*26ed70ecSGuanchao Liang 
432*26ed70ecSGuanchao Liang 	gic_it_set_pending(gd, it);
433*26ed70ecSGuanchao Liang }
434*26ed70ecSGuanchao Liang 
435*26ed70ecSGuanchao Liang static void gic_op_raise_sgi(struct itr_chip *chip, size_t it,
436*26ed70ecSGuanchao Liang 			uint8_t cpu_mask)
437*26ed70ecSGuanchao Liang {
438*26ed70ecSGuanchao Liang 	struct gic_data *gd = container_of(chip, struct gic_data, chip);
439*26ed70ecSGuanchao Liang 
440*26ed70ecSGuanchao Liang 	if (it >= gd->max_it)
441*26ed70ecSGuanchao Liang 		panic();
442*26ed70ecSGuanchao Liang 
443*26ed70ecSGuanchao Liang 	if (it < NUM_NS_SGI)
444*26ed70ecSGuanchao Liang 		gic_it_raise_sgi(gd, it, cpu_mask, 1);
445*26ed70ecSGuanchao Liang 	else
446*26ed70ecSGuanchao Liang 		gic_it_raise_sgi(gd, it, cpu_mask, 0);
447*26ed70ecSGuanchao Liang }
448*26ed70ecSGuanchao Liang static void gic_op_set_affinity(struct itr_chip *chip, size_t it,
449*26ed70ecSGuanchao Liang 			uint8_t cpu_mask)
450*26ed70ecSGuanchao Liang {
451*26ed70ecSGuanchao Liang 	struct gic_data *gd = container_of(chip, struct gic_data, chip);
452*26ed70ecSGuanchao Liang 
453*26ed70ecSGuanchao Liang 	if (it >= gd->max_it)
454*26ed70ecSGuanchao Liang 		panic();
455*26ed70ecSGuanchao Liang 
456*26ed70ecSGuanchao Liang 	gic_it_set_cpu_mask(gd, it, cpu_mask);
457*26ed70ecSGuanchao Liang }
458