xref: /optee_os/core/drivers/gic.c (revision 0ee3f52ed191d21fb12ed5832d4b7929ac7001e4)
11bb92983SJerome Forissier // SPDX-License-Identifier: BSD-2-Clause
2b0104773SPascal Brand /*
318901324SDavid Wang  * Copyright (c) 2016-2017, Linaro Limited
4b0104773SPascal Brand  * Copyright (c) 2014, STMicroelectronics International N.V.
5b0104773SPascal Brand  */
6b0104773SPascal Brand 
718901324SDavid Wang #include <arm.h>
88ddf5a4eSEtienne Carriere #include <assert.h>
967729d8dSLudovic Barre #include <config.h>
1067e55c51SEtienne Carriere #include <compiler.h>
11b0104773SPascal Brand #include <drivers/gic.h>
120f93de74SEtienne Carriere #include <keep.h>
1367729d8dSLudovic Barre #include <kernel/dt.h>
147315b7b4SJens Wiklander #include <kernel/interrupt.h>
15d13278b8SEtienne Carriere #include <kernel/panic.h>
1660801696SVolodymyr Babchuk #include <mm/core_memprot.h>
1760801696SVolodymyr Babchuk #include <mm/core_mmu.h>
1867729d8dSLudovic Barre #include <libfdt.h>
197315b7b4SJens Wiklander #include <util.h>
20b0104773SPascal Brand #include <io.h>
214de4bebcSJens Wiklander #include <trace.h>
22b0104773SPascal Brand 
23b0104773SPascal Brand /* Offsets from gic.gicc_base */
24b0104773SPascal Brand #define GICC_CTLR		(0x000)
2530a673e3SPeter Maydell #define GICC_PMR		(0x004)
26b0104773SPascal Brand #define GICC_IAR		(0x00C)
27b0104773SPascal Brand #define GICC_EOIR		(0x010)
28b0104773SPascal Brand 
29b0104773SPascal Brand #define GICC_CTLR_ENABLEGRP0	(1 << 0)
30b0104773SPascal Brand #define GICC_CTLR_ENABLEGRP1	(1 << 1)
311fcac774SSandeep Tripathy #define GICD_CTLR_ENABLEGRP1S	(1 << 2)
32b0104773SPascal Brand #define GICC_CTLR_FIQEN		(1 << 3)
33b0104773SPascal Brand 
34b0104773SPascal Brand /* Offsets from gic.gicd_base */
35b0104773SPascal Brand #define GICD_CTLR		(0x000)
36b0104773SPascal Brand #define GICD_TYPER		(0x004)
37b0104773SPascal Brand #define GICD_IGROUPR(n)		(0x080 + (n) * 4)
38b0104773SPascal Brand #define GICD_ISENABLER(n)	(0x100 + (n) * 4)
39b0104773SPascal Brand #define GICD_ICENABLER(n)	(0x180 + (n) * 4)
4026ed70ecSGuanchao Liang #define GICD_ISPENDR(n)		(0x200 + (n) * 4)
41b0104773SPascal Brand #define GICD_ICPENDR(n)		(0x280 + (n) * 4)
42b0104773SPascal Brand #define GICD_IPRIORITYR(n)	(0x400 + (n) * 4)
43b0104773SPascal Brand #define GICD_ITARGETSR(n)	(0x800 + (n) * 4)
441fcac774SSandeep Tripathy #define GICD_IGROUPMODR(n)	(0xd00 + (n) * 4)
4526ed70ecSGuanchao Liang #define GICD_SGIR		(0xF00)
46b0104773SPascal Brand 
47b0104773SPascal Brand #define GICD_CTLR_ENABLEGRP0	(1 << 0)
48b0104773SPascal Brand #define GICD_CTLR_ENABLEGRP1	(1 << 1)
49b0104773SPascal Brand 
5053bd332aSSY Chiu /* Number of Private Peripheral Interrupt */
5153bd332aSSY Chiu #define NUM_PPI	32
5253bd332aSSY Chiu 
5326ed70ecSGuanchao Liang /* Number of Software Generated Interrupt */
5426ed70ecSGuanchao Liang #define NUM_SGI			16
5526ed70ecSGuanchao Liang 
5626ed70ecSGuanchao Liang /* Number of Non-secure Software Generated Interrupt */
5726ed70ecSGuanchao Liang #define NUM_NS_SGI		8
5826ed70ecSGuanchao Liang 
5953bd332aSSY Chiu /* Number of interrupts in one register */
6053bd332aSSY Chiu #define NUM_INTS_PER_REG	32
6153bd332aSSY Chiu 
6253bd332aSSY Chiu /* Number of targets in one register */
6353bd332aSSY Chiu #define NUM_TARGETS_PER_REG	4
6453bd332aSSY Chiu 
6553bd332aSSY Chiu /* Accessors to access ITARGETSRn */
6653bd332aSSY Chiu #define ITARGETSR_FIELD_BITS	8
6753bd332aSSY Chiu #define ITARGETSR_FIELD_MASK	0xff
6853bd332aSSY Chiu 
69b0104773SPascal Brand /* Maximum number of interrups a GIC can support */
70b0104773SPascal Brand #define GIC_MAX_INTS		1020
71b0104773SPascal Brand 
727315b7b4SJens Wiklander #define GICC_IAR_IT_ID_MASK	0x3ff
737315b7b4SJens Wiklander #define GICC_IAR_CPU_ID_MASK	0x7
747315b7b4SJens Wiklander #define GICC_IAR_CPU_ID_SHIFT	10
75b0104773SPascal Brand 
7667e55c51SEtienne Carriere struct gic_data {
7767e55c51SEtienne Carriere 	vaddr_t gicc_base;
7867e55c51SEtienne Carriere 	vaddr_t gicd_base;
7967e55c51SEtienne Carriere 	size_t max_it;
8067e55c51SEtienne Carriere 	struct itr_chip chip;
8167e55c51SEtienne Carriere };
8267e55c51SEtienne Carriere 
8367e55c51SEtienne Carriere static struct gic_data gic_data __nex_bss;
8467e55c51SEtienne Carriere 
85702fe5a7SClément Léger static void gic_op_add(struct itr_chip *chip, size_t it, uint32_t type,
86702fe5a7SClément Léger 		       uint32_t prio);
877315b7b4SJens Wiklander static void gic_op_enable(struct itr_chip *chip, size_t it);
887315b7b4SJens Wiklander static void gic_op_disable(struct itr_chip *chip, size_t it);
8926ed70ecSGuanchao Liang static void gic_op_raise_pi(struct itr_chip *chip, size_t it);
9026ed70ecSGuanchao Liang static void gic_op_raise_sgi(struct itr_chip *chip, size_t it,
9126ed70ecSGuanchao Liang 			uint8_t cpu_mask);
9226ed70ecSGuanchao Liang static void gic_op_set_affinity(struct itr_chip *chip, size_t it,
9326ed70ecSGuanchao Liang 			uint8_t cpu_mask);
947315b7b4SJens Wiklander 
957315b7b4SJens Wiklander static const struct itr_ops gic_ops = {
967315b7b4SJens Wiklander 	.add = gic_op_add,
977315b7b4SJens Wiklander 	.enable = gic_op_enable,
987315b7b4SJens Wiklander 	.disable = gic_op_disable,
9926ed70ecSGuanchao Liang 	.raise_pi = gic_op_raise_pi,
10026ed70ecSGuanchao Liang 	.raise_sgi = gic_op_raise_sgi,
10126ed70ecSGuanchao Liang 	.set_affinity = gic_op_set_affinity,
1027315b7b4SJens Wiklander };
1033639b55fSJerome Forissier DECLARE_KEEP_PAGER(gic_ops);
1047315b7b4SJens Wiklander 
10518901324SDavid Wang static size_t probe_max_it(vaddr_t gicc_base __maybe_unused, vaddr_t gicd_base)
106b0104773SPascal Brand {
107b0104773SPascal Brand 	int i;
108b0104773SPascal Brand 	uint32_t old_ctlr;
109b0104773SPascal Brand 	size_t ret = 0;
11079f008d3SJens Wiklander 	const size_t max_regs = ((GIC_MAX_INTS + NUM_INTS_PER_REG - 1) /
11179f008d3SJens Wiklander 					NUM_INTS_PER_REG) - 1;
112b0104773SPascal Brand 
113b0104773SPascal Brand 	/*
114b0104773SPascal Brand 	 * Probe which interrupt number is the largest.
115b0104773SPascal Brand 	 */
11618901324SDavid Wang #if defined(CFG_ARM_GICV3)
11718901324SDavid Wang 	old_ctlr = read_icc_ctlr();
11818901324SDavid Wang 	write_icc_ctlr(0);
11918901324SDavid Wang #else
120918bb3a5SEtienne Carriere 	old_ctlr = io_read32(gicc_base + GICC_CTLR);
121918bb3a5SEtienne Carriere 	io_write32(gicc_base + GICC_CTLR, 0);
12218901324SDavid Wang #endif
12379f008d3SJens Wiklander 	for (i = max_regs; i >= 0; i--) {
124b0104773SPascal Brand 		uint32_t old_reg;
125b0104773SPascal Brand 		uint32_t reg;
126b0104773SPascal Brand 		int b;
127b0104773SPascal Brand 
128918bb3a5SEtienne Carriere 		old_reg = io_read32(gicd_base + GICD_ISENABLER(i));
129918bb3a5SEtienne Carriere 		io_write32(gicd_base + GICD_ISENABLER(i), 0xffffffff);
130918bb3a5SEtienne Carriere 		reg = io_read32(gicd_base + GICD_ISENABLER(i));
131918bb3a5SEtienne Carriere 		io_write32(gicd_base + GICD_ICENABLER(i), ~old_reg);
13279f008d3SJens Wiklander 		for (b = NUM_INTS_PER_REG - 1; b >= 0; b--) {
133007a97a2SJens Wiklander 			if (BIT32(b) & reg) {
13453bd332aSSY Chiu 				ret = i * NUM_INTS_PER_REG + b;
135b0104773SPascal Brand 				goto out;
136b0104773SPascal Brand 			}
137b0104773SPascal Brand 		}
138b0104773SPascal Brand 	}
139b0104773SPascal Brand out:
14018901324SDavid Wang #if defined(CFG_ARM_GICV3)
14118901324SDavid Wang 	write_icc_ctlr(old_ctlr);
14218901324SDavid Wang #else
143918bb3a5SEtienne Carriere 	io_write32(gicc_base + GICC_CTLR, old_ctlr);
14418901324SDavid Wang #endif
145b0104773SPascal Brand 	return ret;
146b0104773SPascal Brand }
147b0104773SPascal Brand 
14867e55c51SEtienne Carriere void gic_cpu_init(void)
149bedc2b9fSsunny {
15067e55c51SEtienne Carriere 	struct gic_data *gd = &gic_data;
15167e55c51SEtienne Carriere 
15218901324SDavid Wang #if defined(CFG_ARM_GICV3)
15318901324SDavid Wang 	assert(gd->gicd_base);
15418901324SDavid Wang #else
15505efe1e1SEtienne Carriere 	assert(gd->gicd_base && gd->gicc_base);
15618901324SDavid Wang #endif
15705efe1e1SEtienne Carriere 
158e06e6e74SPeter Maydell 	/* per-CPU interrupts config:
159bedc2b9fSsunny 	 * ID0-ID7(SGI)   for Non-secure interrupts
160bedc2b9fSsunny 	 * ID8-ID15(SGI)  for Secure interrupts.
161bedc2b9fSsunny 	 * All PPI config as Non-secure interrupts.
162bedc2b9fSsunny 	 */
163918bb3a5SEtienne Carriere 	io_write32(gd->gicd_base + GICD_IGROUPR(0), 0xffff00ff);
164bedc2b9fSsunny 
16530a673e3SPeter Maydell 	/* Set the priority mask to permit Non-secure interrupts, and to
16630a673e3SPeter Maydell 	 * allow the Non-secure world to adjust the priority mask itself
16730a673e3SPeter Maydell 	 */
16818901324SDavid Wang #if defined(CFG_ARM_GICV3)
16918901324SDavid Wang 	write_icc_pmr(0x80);
1701fcac774SSandeep Tripathy 	write_icc_igrpen1(1);
17118901324SDavid Wang #else
172918bb3a5SEtienne Carriere 	io_write32(gd->gicc_base + GICC_PMR, 0x80);
17330a673e3SPeter Maydell 
174bedc2b9fSsunny 	/* Enable GIC */
175918bb3a5SEtienne Carriere 	io_write32(gd->gicc_base + GICC_CTLR,
176918bb3a5SEtienne Carriere 		   GICC_CTLR_ENABLEGRP0 | GICC_CTLR_ENABLEGRP1 |
177918bb3a5SEtienne Carriere 		   GICC_CTLR_FIQEN);
17818901324SDavid Wang #endif
179bedc2b9fSsunny }
180bedc2b9fSsunny 
181*0ee3f52eSEtienne Carriere static int gic_dt_get_irq(const uint32_t *properties, int count, uint32_t *type,
182*0ee3f52eSEtienne Carriere 			  uint32_t *prio)
183*0ee3f52eSEtienne Carriere {
184*0ee3f52eSEtienne Carriere 	int it_num = DT_INFO_INVALID_INTERRUPT;
185*0ee3f52eSEtienne Carriere 
186*0ee3f52eSEtienne Carriere 	if (type)
187*0ee3f52eSEtienne Carriere 		*type = IRQ_TYPE_NONE;
188*0ee3f52eSEtienne Carriere 
189*0ee3f52eSEtienne Carriere 	if (prio)
190*0ee3f52eSEtienne Carriere 		*prio = 0;
191*0ee3f52eSEtienne Carriere 
192*0ee3f52eSEtienne Carriere 	if (!properties || count < 2)
193*0ee3f52eSEtienne Carriere 		return DT_INFO_INVALID_INTERRUPT;
194*0ee3f52eSEtienne Carriere 
195*0ee3f52eSEtienne Carriere 	it_num = fdt32_to_cpu(properties[1]);
196*0ee3f52eSEtienne Carriere 
197*0ee3f52eSEtienne Carriere 	switch (fdt32_to_cpu(properties[0])) {
198*0ee3f52eSEtienne Carriere 	case 1:
199*0ee3f52eSEtienne Carriere 		it_num += 16;
200*0ee3f52eSEtienne Carriere 		break;
201*0ee3f52eSEtienne Carriere 	case 0:
202*0ee3f52eSEtienne Carriere 		it_num += 32;
203*0ee3f52eSEtienne Carriere 		break;
204*0ee3f52eSEtienne Carriere 	default:
205*0ee3f52eSEtienne Carriere 		it_num = DT_INFO_INVALID_INTERRUPT;
206*0ee3f52eSEtienne Carriere 	}
207*0ee3f52eSEtienne Carriere 
208*0ee3f52eSEtienne Carriere 	return it_num;
209*0ee3f52eSEtienne Carriere }
210*0ee3f52eSEtienne Carriere 
211*0ee3f52eSEtienne Carriere static void gic_init_base_addr(paddr_t gicc_base_pa, paddr_t gicd_base_pa)
212b0104773SPascal Brand {
21367e55c51SEtienne Carriere 	struct gic_data *gd = &gic_data;
214*0ee3f52eSEtienne Carriere 	vaddr_t gicc_base = 0;
215*0ee3f52eSEtienne Carriere 	vaddr_t gicd_base = 0;
216*0ee3f52eSEtienne Carriere 
217*0ee3f52eSEtienne Carriere 	assert(cpu_mmu_enabled());
218*0ee3f52eSEtienne Carriere 
219*0ee3f52eSEtienne Carriere 	gicd_base = core_mmu_get_va(gicd_base_pa, MEM_AREA_IO_SEC,
220*0ee3f52eSEtienne Carriere 				    GIC_DIST_REG_SIZE);
221*0ee3f52eSEtienne Carriere 	if (!gicd_base)
222*0ee3f52eSEtienne Carriere 		panic();
223*0ee3f52eSEtienne Carriere 
224*0ee3f52eSEtienne Carriere 	if (!IS_ENABLED(CFG_ARM_GICV3)) {
225*0ee3f52eSEtienne Carriere 		gicc_base = core_mmu_get_va(gicc_base_pa, MEM_AREA_IO_SEC,
226*0ee3f52eSEtienne Carriere 					    GIC_CPU_REG_SIZE);
227*0ee3f52eSEtienne Carriere 		if (!gicc_base)
228*0ee3f52eSEtienne Carriere 			panic();
229*0ee3f52eSEtienne Carriere 	}
230*0ee3f52eSEtienne Carriere 
231*0ee3f52eSEtienne Carriere 	gd->gicc_base = gicc_base;
232*0ee3f52eSEtienne Carriere 	gd->gicd_base = gicd_base;
233*0ee3f52eSEtienne Carriere 	gd->max_it = probe_max_it(gicc_base, gicd_base);
234*0ee3f52eSEtienne Carriere 	gd->chip.ops = &gic_ops;
235*0ee3f52eSEtienne Carriere 
236*0ee3f52eSEtienne Carriere 	if (IS_ENABLED(CFG_DT))
237*0ee3f52eSEtienne Carriere 		gd->chip.dt_get_irq = gic_dt_get_irq;
238*0ee3f52eSEtienne Carriere }
239*0ee3f52eSEtienne Carriere 
240*0ee3f52eSEtienne Carriere void gic_init(paddr_t gicc_base_pa, paddr_t gicd_base_pa)
241*0ee3f52eSEtienne Carriere {
242*0ee3f52eSEtienne Carriere 	struct gic_data __maybe_unused *gd = &gic_data;
243*0ee3f52eSEtienne Carriere 	size_t __maybe_unused n = 0;
244b0104773SPascal Brand 
24567e55c51SEtienne Carriere 	gic_init_base_addr(gicc_base_pa, gicd_base_pa);
246b0104773SPascal Brand 
247*0ee3f52eSEtienne Carriere 	/* GIC configuration is initialized from TF-A when embedded */
248*0ee3f52eSEtienne Carriere #ifndef CFG_WITH_ARM_TRUSTED_FW
2497315b7b4SJens Wiklander 	for (n = 0; n <= gd->max_it / NUM_INTS_PER_REG; n++) {
250b0104773SPascal Brand 		/* Disable interrupts */
251918bb3a5SEtienne Carriere 		io_write32(gd->gicd_base + GICD_ICENABLER(n), 0xffffffff);
252b0104773SPascal Brand 
253b0104773SPascal Brand 		/* Make interrupts non-pending */
254918bb3a5SEtienne Carriere 		io_write32(gd->gicd_base + GICD_ICPENDR(n), 0xffffffff);
255b0104773SPascal Brand 
256b0104773SPascal Brand 		/* Mark interrupts non-secure */
257bedc2b9fSsunny 		if (n == 0) {
258bedc2b9fSsunny 			/* per-CPU inerrupts config:
259bedc2b9fSsunny 			 * ID0-ID7(SGI)	  for Non-secure interrupts
260bedc2b9fSsunny 			 * ID8-ID15(SGI)  for Secure interrupts.
261bedc2b9fSsunny 			 * All PPI config as Non-secure interrupts.
262bedc2b9fSsunny 			 */
263918bb3a5SEtienne Carriere 			io_write32(gd->gicd_base + GICD_IGROUPR(n), 0xffff00ff);
264bedc2b9fSsunny 		} else {
265918bb3a5SEtienne Carriere 			io_write32(gd->gicd_base + GICD_IGROUPR(n), 0xffffffff);
266b0104773SPascal Brand 		}
267bedc2b9fSsunny 	}
268b0104773SPascal Brand 
26930a673e3SPeter Maydell 	/* Set the priority mask to permit Non-secure interrupts, and to
27030a673e3SPeter Maydell 	 * allow the Non-secure world to adjust the priority mask itself
27130a673e3SPeter Maydell 	 */
27218901324SDavid Wang #if defined(CFG_ARM_GICV3)
27318901324SDavid Wang 	write_icc_pmr(0x80);
2741fcac774SSandeep Tripathy 	write_icc_igrpen1(1);
2751fcac774SSandeep Tripathy 	io_setbits32(gd->gicd_base + GICD_CTLR, GICD_CTLR_ENABLEGRP1S);
27618901324SDavid Wang #else
277918bb3a5SEtienne Carriere 	io_write32(gd->gicc_base + GICC_PMR, 0x80);
27830a673e3SPeter Maydell 
279b0104773SPascal Brand 	/* Enable GIC */
280918bb3a5SEtienne Carriere 	io_write32(gd->gicc_base + GICC_CTLR, GICC_CTLR_FIQEN |
281918bb3a5SEtienne Carriere 		   GICC_CTLR_ENABLEGRP0 | GICC_CTLR_ENABLEGRP1);
282918bb3a5SEtienne Carriere 	io_setbits32(gd->gicd_base + GICD_CTLR,
283918bb3a5SEtienne Carriere 		     GICD_CTLR_ENABLEGRP0 | GICD_CTLR_ENABLEGRP1);
2841fcac774SSandeep Tripathy #endif
285*0ee3f52eSEtienne Carriere #endif /*CFG_WITH_ARM_TRUSTED_FW*/
28667e55c51SEtienne Carriere 
28767e55c51SEtienne Carriere 	itr_init(&gic_data.chip);
288b0104773SPascal Brand }
289b0104773SPascal Brand 
2907315b7b4SJens Wiklander static void gic_it_add(struct gic_data *gd, size_t it)
291b0104773SPascal Brand {
29253bd332aSSY Chiu 	size_t idx = it / NUM_INTS_PER_REG;
29353bd332aSSY Chiu 	uint32_t mask = 1 << (it % NUM_INTS_PER_REG);
294b0104773SPascal Brand 
29567e55c51SEtienne Carriere 	assert(gd == &gic_data);
29667e55c51SEtienne Carriere 
297b0104773SPascal Brand 	/* Disable the interrupt */
298918bb3a5SEtienne Carriere 	io_write32(gd->gicd_base + GICD_ICENABLER(idx), mask);
299b0104773SPascal Brand 	/* Make it non-pending */
300918bb3a5SEtienne Carriere 	io_write32(gd->gicd_base + GICD_ICPENDR(idx), mask);
301b0104773SPascal Brand 	/* Assign it to group0 */
302918bb3a5SEtienne Carriere 	io_clrbits32(gd->gicd_base + GICD_IGROUPR(idx), mask);
3031fcac774SSandeep Tripathy #if defined(CFG_ARM_GICV3)
3041fcac774SSandeep Tripathy 	/* Assign it to group1S */
3051fcac774SSandeep Tripathy 	io_setbits32(gd->gicd_base + GICD_IGROUPMODR(idx), mask);
3061fcac774SSandeep Tripathy #endif
307b0104773SPascal Brand }
308b0104773SPascal Brand 
3097315b7b4SJens Wiklander static void gic_it_set_cpu_mask(struct gic_data *gd, size_t it,
3107315b7b4SJens Wiklander 				uint8_t cpu_mask)
311b0104773SPascal Brand {
3128ddf5a4eSEtienne Carriere 	size_t idx __maybe_unused = it / NUM_INTS_PER_REG;
3138ddf5a4eSEtienne Carriere 	uint32_t mask __maybe_unused = 1 << (it % NUM_INTS_PER_REG);
31453bd332aSSY Chiu 	uint32_t target, target_shift;
315918bb3a5SEtienne Carriere 	vaddr_t itargetsr = gd->gicd_base +
316918bb3a5SEtienne Carriere 			    GICD_ITARGETSR(it / NUM_TARGETS_PER_REG);
317b0104773SPascal Brand 
31867e55c51SEtienne Carriere 	assert(gd == &gic_data);
31967e55c51SEtienne Carriere 
320b0104773SPascal Brand 	/* Assigned to group0 */
321918bb3a5SEtienne Carriere 	assert(!(io_read32(gd->gicd_base + GICD_IGROUPR(idx)) & mask));
322b0104773SPascal Brand 
323b0104773SPascal Brand 	/* Route it to selected CPUs */
324918bb3a5SEtienne Carriere 	target = io_read32(itargetsr);
32553bd332aSSY Chiu 	target_shift = (it % NUM_TARGETS_PER_REG) * ITARGETSR_FIELD_BITS;
32653bd332aSSY Chiu 	target &= ~(ITARGETSR_FIELD_MASK << target_shift);
32753bd332aSSY Chiu 	target |= cpu_mask << target_shift;
328918bb3a5SEtienne Carriere 	DMSG("cpu_mask: writing 0x%x to 0x%" PRIxVA, target, itargetsr);
329918bb3a5SEtienne Carriere 	io_write32(itargetsr, target);
330918bb3a5SEtienne Carriere 	DMSG("cpu_mask: 0x%x", io_read32(itargetsr));
331b0104773SPascal Brand }
332b0104773SPascal Brand 
3337315b7b4SJens Wiklander static void gic_it_set_prio(struct gic_data *gd, size_t it, uint8_t prio)
334b0104773SPascal Brand {
3358ddf5a4eSEtienne Carriere 	size_t idx __maybe_unused = it / NUM_INTS_PER_REG;
3368ddf5a4eSEtienne Carriere 	uint32_t mask __maybe_unused = 1 << (it % NUM_INTS_PER_REG);
337b0104773SPascal Brand 
33867e55c51SEtienne Carriere 	assert(gd == &gic_data);
33967e55c51SEtienne Carriere 
340b0104773SPascal Brand 	/* Assigned to group0 */
341918bb3a5SEtienne Carriere 	assert(!(io_read32(gd->gicd_base + GICD_IGROUPR(idx)) & mask));
342b0104773SPascal Brand 
343b0104773SPascal Brand 	/* Set prio it to selected CPUs */
3441f60363aSJens Wiklander 	DMSG("prio: writing 0x%x to 0x%" PRIxVA,
3457315b7b4SJens Wiklander 		prio, gd->gicd_base + GICD_IPRIORITYR(0) + it);
346918bb3a5SEtienne Carriere 	io_write8(gd->gicd_base + GICD_IPRIORITYR(0) + it, prio);
347b0104773SPascal Brand }
348b0104773SPascal Brand 
3497315b7b4SJens Wiklander static void gic_it_enable(struct gic_data *gd, size_t it)
350b0104773SPascal Brand {
35153bd332aSSY Chiu 	size_t idx = it / NUM_INTS_PER_REG;
35253bd332aSSY Chiu 	uint32_t mask = 1 << (it % NUM_INTS_PER_REG);
353918bb3a5SEtienne Carriere 	vaddr_t base = gd->gicd_base;
354b0104773SPascal Brand 
35567e55c51SEtienne Carriere 	assert(gd == &gic_data);
35667e55c51SEtienne Carriere 
357b0104773SPascal Brand 	/* Assigned to group0 */
358918bb3a5SEtienne Carriere 	assert(!(io_read32(base + GICD_IGROUPR(idx)) & mask));
359b0104773SPascal Brand 
360b0104773SPascal Brand 	/* Enable the interrupt */
361918bb3a5SEtienne Carriere 	io_write32(base + GICD_ISENABLER(idx), mask);
362b0104773SPascal Brand }
363b0104773SPascal Brand 
3647315b7b4SJens Wiklander static void gic_it_disable(struct gic_data *gd, size_t it)
365b0104773SPascal Brand {
36653bd332aSSY Chiu 	size_t idx = it / NUM_INTS_PER_REG;
36753bd332aSSY Chiu 	uint32_t mask = 1 << (it % NUM_INTS_PER_REG);
368b0104773SPascal Brand 
36967e55c51SEtienne Carriere 	assert(gd == &gic_data);
37067e55c51SEtienne Carriere 
371b0104773SPascal Brand 	/* Assigned to group0 */
372918bb3a5SEtienne Carriere 	assert(!(io_read32(gd->gicd_base + GICD_IGROUPR(idx)) & mask));
373b0104773SPascal Brand 
374b0104773SPascal Brand 	/* Disable the interrupt */
375918bb3a5SEtienne Carriere 	io_write32(gd->gicd_base + GICD_ICENABLER(idx), mask);
376b0104773SPascal Brand }
377b0104773SPascal Brand 
37826ed70ecSGuanchao Liang static void gic_it_set_pending(struct gic_data *gd, size_t it)
37926ed70ecSGuanchao Liang {
38026ed70ecSGuanchao Liang 	size_t idx = it / NUM_INTS_PER_REG;
38126ed70ecSGuanchao Liang 	uint32_t mask = BIT32(it % NUM_INTS_PER_REG);
38226ed70ecSGuanchao Liang 
38367e55c51SEtienne Carriere 	assert(gd == &gic_data);
38467e55c51SEtienne Carriere 
38526ed70ecSGuanchao Liang 	/* Should be Peripheral Interrupt */
38626ed70ecSGuanchao Liang 	assert(it >= NUM_SGI);
38726ed70ecSGuanchao Liang 
38826ed70ecSGuanchao Liang 	/* Raise the interrupt */
389918bb3a5SEtienne Carriere 	io_write32(gd->gicd_base + GICD_ISPENDR(idx), mask);
39026ed70ecSGuanchao Liang }
39126ed70ecSGuanchao Liang 
39226ed70ecSGuanchao Liang static void gic_it_raise_sgi(struct gic_data *gd, size_t it,
39326ed70ecSGuanchao Liang 		uint8_t cpu_mask, uint8_t group)
39426ed70ecSGuanchao Liang {
39526ed70ecSGuanchao Liang 	uint32_t mask_id = it & 0xf;
39626ed70ecSGuanchao Liang 	uint32_t mask_group = group & 0x1;
39726ed70ecSGuanchao Liang 	uint32_t mask_cpu = cpu_mask & 0xff;
39826ed70ecSGuanchao Liang 	uint32_t mask = (mask_id | SHIFT_U32(mask_group, 15) |
39926ed70ecSGuanchao Liang 		SHIFT_U32(mask_cpu, 16));
40026ed70ecSGuanchao Liang 
40167e55c51SEtienne Carriere 	assert(gd == &gic_data);
40267e55c51SEtienne Carriere 
40326ed70ecSGuanchao Liang 	/* Should be Software Generated Interrupt */
40426ed70ecSGuanchao Liang 	assert(it < NUM_SGI);
40526ed70ecSGuanchao Liang 
40626ed70ecSGuanchao Liang 	/* Raise the interrupt */
407918bb3a5SEtienne Carriere 	io_write32(gd->gicd_base + GICD_SGIR, mask);
40826ed70ecSGuanchao Liang }
40926ed70ecSGuanchao Liang 
41018901324SDavid Wang static uint32_t gic_read_iar(struct gic_data *gd __maybe_unused)
411b0104773SPascal Brand {
41267e55c51SEtienne Carriere 	assert(gd == &gic_data);
41367e55c51SEtienne Carriere 
41418901324SDavid Wang #if defined(CFG_ARM_GICV3)
4151de462e1SSumit Garg 	return read_icc_iar1();
41618901324SDavid Wang #else
417918bb3a5SEtienne Carriere 	return io_read32(gd->gicc_base + GICC_IAR);
41818901324SDavid Wang #endif
419b0104773SPascal Brand }
420b0104773SPascal Brand 
42118901324SDavid Wang static void gic_write_eoir(struct gic_data *gd __maybe_unused, uint32_t eoir)
422b0104773SPascal Brand {
42367e55c51SEtienne Carriere 	assert(gd == &gic_data);
42467e55c51SEtienne Carriere 
42518901324SDavid Wang #if defined(CFG_ARM_GICV3)
4261de462e1SSumit Garg 	write_icc_eoir1(eoir);
42718901324SDavid Wang #else
428918bb3a5SEtienne Carriere 	io_write32(gd->gicc_base + GICC_EOIR, eoir);
42918901324SDavid Wang #endif
430b0104773SPascal Brand }
431b0104773SPascal Brand 
4327315b7b4SJens Wiklander static bool gic_it_is_enabled(struct gic_data *gd, size_t it)
4337315b7b4SJens Wiklander {
43453bd332aSSY Chiu 	size_t idx = it / NUM_INTS_PER_REG;
43553bd332aSSY Chiu 	uint32_t mask = 1 << (it % NUM_INTS_PER_REG);
43667e55c51SEtienne Carriere 
43767e55c51SEtienne Carriere 	assert(gd == &gic_data);
438918bb3a5SEtienne Carriere 	return !!(io_read32(gd->gicd_base + GICD_ISENABLER(idx)) & mask);
43953bd332aSSY Chiu }
44053bd332aSSY Chiu 
4417315b7b4SJens Wiklander static bool __maybe_unused gic_it_get_group(struct gic_data *gd, size_t it)
4427315b7b4SJens Wiklander {
44353bd332aSSY Chiu 	size_t idx = it / NUM_INTS_PER_REG;
44453bd332aSSY Chiu 	uint32_t mask = 1 << (it % NUM_INTS_PER_REG);
44567e55c51SEtienne Carriere 
44667e55c51SEtienne Carriere 	assert(gd == &gic_data);
447918bb3a5SEtienne Carriere 	return !!(io_read32(gd->gicd_base + GICD_IGROUPR(idx)) & mask);
44853bd332aSSY Chiu }
44953bd332aSSY Chiu 
4507315b7b4SJens Wiklander static uint32_t __maybe_unused gic_it_get_target(struct gic_data *gd, size_t it)
4517315b7b4SJens Wiklander {
45253bd332aSSY Chiu 	size_t reg_idx = it / NUM_TARGETS_PER_REG;
4537315b7b4SJens Wiklander 	uint32_t target_shift = (it % NUM_TARGETS_PER_REG) *
4547315b7b4SJens Wiklander 				ITARGETSR_FIELD_BITS;
45553bd332aSSY Chiu 	uint32_t target_mask = ITARGETSR_FIELD_MASK << target_shift;
456918bb3a5SEtienne Carriere 	uint32_t target = io_read32(gd->gicd_base + GICD_ITARGETSR(reg_idx));
4577315b7b4SJens Wiklander 
45867e55c51SEtienne Carriere 	assert(gd == &gic_data);
459918bb3a5SEtienne Carriere 	return (target & target_mask) >> target_shift;
46053bd332aSSY Chiu }
46153bd332aSSY Chiu 
46267e55c51SEtienne Carriere void gic_dump_state(void)
46353bd332aSSY Chiu {
46467e55c51SEtienne Carriere 	struct gic_data *gd = &gic_data;
46567e55c51SEtienne Carriere 	int i = 0;
46653bd332aSSY Chiu 
46718901324SDavid Wang #if defined(CFG_ARM_GICV3)
46818901324SDavid Wang 	DMSG("GICC_CTLR: 0x%x", read_icc_ctlr());
46918901324SDavid Wang #else
470918bb3a5SEtienne Carriere 	DMSG("GICC_CTLR: 0x%x", io_read32(gd->gicc_base + GICC_CTLR));
47118901324SDavid Wang #endif
472918bb3a5SEtienne Carriere 	DMSG("GICD_CTLR: 0x%x", io_read32(gd->gicd_base + GICD_CTLR));
4737315b7b4SJens Wiklander 
4744a9ea08cSFangsuo Wu 	for (i = 0; i <= (int)gd->max_it; i++) {
4757315b7b4SJens Wiklander 		if (gic_it_is_enabled(gd, i)) {
47653bd332aSSY Chiu 			DMSG("irq%d: enabled, group:%d, target:%x", i,
4777315b7b4SJens Wiklander 			     gic_it_get_group(gd, i), gic_it_get_target(gd, i));
47853bd332aSSY Chiu 		}
47953bd332aSSY Chiu 	}
48053bd332aSSY Chiu }
4817315b7b4SJens Wiklander 
48267e55c51SEtienne Carriere static void __maybe_unused gic_native_itr_handler(void)
4837315b7b4SJens Wiklander {
48467e55c51SEtienne Carriere 	struct gic_data *gd = &gic_data;
48567e55c51SEtienne Carriere 	uint32_t iar = 0;
48667e55c51SEtienne Carriere 	uint32_t id = 0;
4877315b7b4SJens Wiklander 
4887315b7b4SJens Wiklander 	iar = gic_read_iar(gd);
4897315b7b4SJens Wiklander 	id = iar & GICC_IAR_IT_ID_MASK;
4907315b7b4SJens Wiklander 
4914a9ea08cSFangsuo Wu 	if (id <= gd->max_it)
4927315b7b4SJens Wiklander 		itr_handle(id);
4933b3a4611SMathieu Briand 	else
4943b3a4611SMathieu Briand 		DMSG("ignoring interrupt %" PRIu32, id);
4957315b7b4SJens Wiklander 
4967315b7b4SJens Wiklander 	gic_write_eoir(gd, iar);
4977315b7b4SJens Wiklander }
4987315b7b4SJens Wiklander 
49967e55c51SEtienne Carriere #ifndef CFG_CORE_WORKAROUND_ARM_NMFI
50067e55c51SEtienne Carriere /* Override itr_core_handler() with core interrupt controller implementation */
50167e55c51SEtienne Carriere void itr_core_handler(void)
50267e55c51SEtienne Carriere {
50367e55c51SEtienne Carriere 	gic_native_itr_handler();
50467e55c51SEtienne Carriere }
50567e55c51SEtienne Carriere #endif /*CFG_CORE_WORKAROUND_ARM_NMFI*/
50667e55c51SEtienne Carriere 
5077315b7b4SJens Wiklander static void gic_op_add(struct itr_chip *chip, size_t it,
508702fe5a7SClément Léger 		       uint32_t type __unused,
509702fe5a7SClément Léger 		       uint32_t prio __unused)
5107315b7b4SJens Wiklander {
5117315b7b4SJens Wiklander 	struct gic_data *gd = container_of(chip, struct gic_data, chip);
5127315b7b4SJens Wiklander 
51367e55c51SEtienne Carriere 	assert(gd == &gic_data);
51467e55c51SEtienne Carriere 
5154a9ea08cSFangsuo Wu 	if (it > gd->max_it)
516d13278b8SEtienne Carriere 		panic();
517d13278b8SEtienne Carriere 
5187315b7b4SJens Wiklander 	gic_it_add(gd, it);
5197315b7b4SJens Wiklander 	/* Set the CPU mask to deliver interrupts to any online core */
5207315b7b4SJens Wiklander 	gic_it_set_cpu_mask(gd, it, 0xff);
5217315b7b4SJens Wiklander 	gic_it_set_prio(gd, it, 0x1);
5227315b7b4SJens Wiklander }
5237315b7b4SJens Wiklander 
5247315b7b4SJens Wiklander static void gic_op_enable(struct itr_chip *chip, size_t it)
5257315b7b4SJens Wiklander {
5267315b7b4SJens Wiklander 	struct gic_data *gd = container_of(chip, struct gic_data, chip);
5277315b7b4SJens Wiklander 
52867e55c51SEtienne Carriere 	assert(gd == &gic_data);
52967e55c51SEtienne Carriere 
5304a9ea08cSFangsuo Wu 	if (it > gd->max_it)
531d13278b8SEtienne Carriere 		panic();
532d13278b8SEtienne Carriere 
5337315b7b4SJens Wiklander 	gic_it_enable(gd, it);
5347315b7b4SJens Wiklander }
5357315b7b4SJens Wiklander 
5367315b7b4SJens Wiklander static void gic_op_disable(struct itr_chip *chip, size_t it)
5377315b7b4SJens Wiklander {
5387315b7b4SJens Wiklander 	struct gic_data *gd = container_of(chip, struct gic_data, chip);
5397315b7b4SJens Wiklander 
54067e55c51SEtienne Carriere 	assert(gd == &gic_data);
54167e55c51SEtienne Carriere 
5424a9ea08cSFangsuo Wu 	if (it > gd->max_it)
543d13278b8SEtienne Carriere 		panic();
544d13278b8SEtienne Carriere 
5457315b7b4SJens Wiklander 	gic_it_disable(gd, it);
5467315b7b4SJens Wiklander }
54726ed70ecSGuanchao Liang 
54826ed70ecSGuanchao Liang static void gic_op_raise_pi(struct itr_chip *chip, size_t it)
54926ed70ecSGuanchao Liang {
55026ed70ecSGuanchao Liang 	struct gic_data *gd = container_of(chip, struct gic_data, chip);
55126ed70ecSGuanchao Liang 
55267e55c51SEtienne Carriere 	assert(gd == &gic_data);
55367e55c51SEtienne Carriere 
5544a9ea08cSFangsuo Wu 	if (it > gd->max_it)
55526ed70ecSGuanchao Liang 		panic();
55626ed70ecSGuanchao Liang 
55726ed70ecSGuanchao Liang 	gic_it_set_pending(gd, it);
55826ed70ecSGuanchao Liang }
55926ed70ecSGuanchao Liang 
56026ed70ecSGuanchao Liang static void gic_op_raise_sgi(struct itr_chip *chip, size_t it,
56126ed70ecSGuanchao Liang 			uint8_t cpu_mask)
56226ed70ecSGuanchao Liang {
56326ed70ecSGuanchao Liang 	struct gic_data *gd = container_of(chip, struct gic_data, chip);
56426ed70ecSGuanchao Liang 
56567e55c51SEtienne Carriere 	assert(gd == &gic_data);
56667e55c51SEtienne Carriere 
5674a9ea08cSFangsuo Wu 	if (it > gd->max_it)
56826ed70ecSGuanchao Liang 		panic();
56926ed70ecSGuanchao Liang 
57026ed70ecSGuanchao Liang 	if (it < NUM_NS_SGI)
57126ed70ecSGuanchao Liang 		gic_it_raise_sgi(gd, it, cpu_mask, 1);
57226ed70ecSGuanchao Liang 	else
57326ed70ecSGuanchao Liang 		gic_it_raise_sgi(gd, it, cpu_mask, 0);
57426ed70ecSGuanchao Liang }
57567e55c51SEtienne Carriere 
57626ed70ecSGuanchao Liang static void gic_op_set_affinity(struct itr_chip *chip, size_t it,
57726ed70ecSGuanchao Liang 			uint8_t cpu_mask)
57826ed70ecSGuanchao Liang {
57926ed70ecSGuanchao Liang 	struct gic_data *gd = container_of(chip, struct gic_data, chip);
58026ed70ecSGuanchao Liang 
58167e55c51SEtienne Carriere 	assert(gd == &gic_data);
58267e55c51SEtienne Carriere 
5834a9ea08cSFangsuo Wu 	if (it > gd->max_it)
58426ed70ecSGuanchao Liang 		panic();
58526ed70ecSGuanchao Liang 
58626ed70ecSGuanchao Liang 	gic_it_set_cpu_mask(gd, it, cpu_mask);
58726ed70ecSGuanchao Liang }
588