xref: /optee_os/core/drivers/gic.c (revision 67e55c51c9149ea549664b3981ad9032dcf4ce7f)
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>
10*67e55c51SEtienne 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 
76*67e55c51SEtienne Carriere struct gic_data {
77*67e55c51SEtienne Carriere 	vaddr_t gicc_base;
78*67e55c51SEtienne Carriere 	vaddr_t gicd_base;
79*67e55c51SEtienne Carriere 	size_t max_it;
80*67e55c51SEtienne Carriere 	struct itr_chip chip;
81*67e55c51SEtienne Carriere };
82*67e55c51SEtienne Carriere 
83*67e55c51SEtienne Carriere static struct gic_data gic_data __nex_bss;
84*67e55c51SEtienne 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 
148*67e55c51SEtienne Carriere void gic_cpu_init(void)
149bedc2b9fSsunny {
150*67e55c51SEtienne Carriere 	struct gic_data *gd = &gic_data;
151*67e55c51SEtienne 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*67e55c51SEtienne Carriere void gic_init(paddr_t gicc_base_pa, paddr_t gicd_base_pa)
182b0104773SPascal Brand {
183*67e55c51SEtienne Carriere 	struct gic_data *gd = &gic_data;
184b0104773SPascal Brand 	size_t n;
185b0104773SPascal Brand 
186*67e55c51SEtienne Carriere 	gic_init_base_addr(gicc_base_pa, gicd_base_pa);
187b0104773SPascal Brand 
1887315b7b4SJens Wiklander 	for (n = 0; n <= gd->max_it / NUM_INTS_PER_REG; n++) {
189b0104773SPascal Brand 		/* Disable interrupts */
190918bb3a5SEtienne Carriere 		io_write32(gd->gicd_base + GICD_ICENABLER(n), 0xffffffff);
191b0104773SPascal Brand 
192b0104773SPascal Brand 		/* Make interrupts non-pending */
193918bb3a5SEtienne Carriere 		io_write32(gd->gicd_base + GICD_ICPENDR(n), 0xffffffff);
194b0104773SPascal Brand 
195b0104773SPascal Brand 		/* Mark interrupts non-secure */
196bedc2b9fSsunny 		if (n == 0) {
197bedc2b9fSsunny 			/* per-CPU inerrupts config:
198bedc2b9fSsunny 			 * ID0-ID7(SGI)	  for Non-secure interrupts
199bedc2b9fSsunny 			 * ID8-ID15(SGI)  for Secure interrupts.
200bedc2b9fSsunny 			 * All PPI config as Non-secure interrupts.
201bedc2b9fSsunny 			 */
202918bb3a5SEtienne Carriere 			io_write32(gd->gicd_base + GICD_IGROUPR(n), 0xffff00ff);
203bedc2b9fSsunny 		} else {
204918bb3a5SEtienne Carriere 			io_write32(gd->gicd_base + GICD_IGROUPR(n), 0xffffffff);
205b0104773SPascal Brand 		}
206bedc2b9fSsunny 	}
207b0104773SPascal Brand 
20830a673e3SPeter Maydell 	/* Set the priority mask to permit Non-secure interrupts, and to
20930a673e3SPeter Maydell 	 * allow the Non-secure world to adjust the priority mask itself
21030a673e3SPeter Maydell 	 */
21118901324SDavid Wang #if defined(CFG_ARM_GICV3)
21218901324SDavid Wang 	write_icc_pmr(0x80);
2131fcac774SSandeep Tripathy 	write_icc_igrpen1(1);
2141fcac774SSandeep Tripathy 	io_setbits32(gd->gicd_base + GICD_CTLR, GICD_CTLR_ENABLEGRP1S);
21518901324SDavid Wang #else
216918bb3a5SEtienne Carriere 	io_write32(gd->gicc_base + GICC_PMR, 0x80);
21730a673e3SPeter Maydell 
218b0104773SPascal Brand 	/* Enable GIC */
219918bb3a5SEtienne Carriere 	io_write32(gd->gicc_base + GICC_CTLR, GICC_CTLR_FIQEN |
220918bb3a5SEtienne Carriere 		   GICC_CTLR_ENABLEGRP0 | GICC_CTLR_ENABLEGRP1);
221918bb3a5SEtienne Carriere 	io_setbits32(gd->gicd_base + GICD_CTLR,
222918bb3a5SEtienne Carriere 		     GICD_CTLR_ENABLEGRP0 | GICD_CTLR_ENABLEGRP1);
2231fcac774SSandeep Tripathy #endif
224*67e55c51SEtienne Carriere 
225*67e55c51SEtienne Carriere 	itr_init(&gic_data.chip);
226b0104773SPascal Brand }
227b0104773SPascal Brand 
228702fe5a7SClément Léger static int gic_dt_get_irq(const uint32_t *properties, int count, uint32_t *type,
229702fe5a7SClément Léger 			  uint32_t *prio)
23067729d8dSLudovic Barre {
231ed74d1c4SLudovic Barre 	int it_num = DT_INFO_INVALID_INTERRUPT;
232ed74d1c4SLudovic Barre 
233702fe5a7SClément Léger 	if (type)
234702fe5a7SClément Léger 		*type = IRQ_TYPE_NONE;
235702fe5a7SClément Léger 
236702fe5a7SClément Léger 	if (prio)
237702fe5a7SClément Léger 		*prio = 0;
238702fe5a7SClément Léger 
239888bb63dSClément Léger 	if (!properties || count < 2)
24067729d8dSLudovic Barre 		return DT_INFO_INVALID_INTERRUPT;
24167729d8dSLudovic Barre 
242ed74d1c4SLudovic Barre 	it_num = fdt32_to_cpu(properties[1]);
243ed74d1c4SLudovic Barre 
244ed74d1c4SLudovic Barre 	switch (fdt32_to_cpu(properties[0])) {
245ed74d1c4SLudovic Barre 	case 1:
246ed74d1c4SLudovic Barre 		it_num += 16;
247ed74d1c4SLudovic Barre 		break;
248ed74d1c4SLudovic Barre 	case 0:
249ed74d1c4SLudovic Barre 		it_num += 32;
250ed74d1c4SLudovic Barre 		break;
251ed74d1c4SLudovic Barre 	default:
252ed74d1c4SLudovic Barre 		it_num = DT_INFO_INVALID_INTERRUPT;
253ed74d1c4SLudovic Barre 	}
254ed74d1c4SLudovic Barre 
255ed74d1c4SLudovic Barre 	return it_num;
25667729d8dSLudovic Barre }
25767729d8dSLudovic Barre 
258*67e55c51SEtienne Carriere void gic_init_base_addr(paddr_t gicc_base_pa, paddr_t gicd_base_pa)
25953bd332aSSY Chiu {
260*67e55c51SEtienne Carriere 	struct gic_data *gd = &gic_data;
26160801696SVolodymyr Babchuk 	vaddr_t gicc_base = 0;
26260801696SVolodymyr Babchuk 	vaddr_t gicd_base = 0;
26360801696SVolodymyr Babchuk 
26460801696SVolodymyr Babchuk 	assert(cpu_mmu_enabled());
26560801696SVolodymyr Babchuk 
26660801696SVolodymyr Babchuk 	gicd_base = core_mmu_get_va(gicd_base_pa, MEM_AREA_IO_SEC,
26760801696SVolodymyr Babchuk 				    GIC_DIST_REG_SIZE);
26860801696SVolodymyr Babchuk 	if (!gicd_base)
26960801696SVolodymyr Babchuk 		panic();
27060801696SVolodymyr Babchuk 
27160801696SVolodymyr Babchuk 	if (!IS_ENABLED(CFG_ARM_GICV3)) {
27260801696SVolodymyr Babchuk 		gicc_base = core_mmu_get_va(gicc_base_pa, MEM_AREA_IO_SEC,
27360801696SVolodymyr Babchuk 					    GIC_CPU_REG_SIZE);
27460801696SVolodymyr Babchuk 		if (!gicc_base)
27560801696SVolodymyr Babchuk 			panic();
27660801696SVolodymyr Babchuk 	}
27760801696SVolodymyr Babchuk 
2787315b7b4SJens Wiklander 	gd->gicc_base = gicc_base;
2797315b7b4SJens Wiklander 	gd->gicd_base = gicd_base;
2807315b7b4SJens Wiklander 	gd->max_it = probe_max_it(gicc_base, gicd_base);
2817315b7b4SJens Wiklander 	gd->chip.ops = &gic_ops;
28267729d8dSLudovic Barre 
28367729d8dSLudovic Barre 	if (IS_ENABLED(CFG_DT))
28467729d8dSLudovic Barre 		gd->chip.dt_get_irq = gic_dt_get_irq;
28553bd332aSSY Chiu }
28653bd332aSSY Chiu 
2877315b7b4SJens Wiklander static void gic_it_add(struct gic_data *gd, size_t it)
288b0104773SPascal Brand {
28953bd332aSSY Chiu 	size_t idx = it / NUM_INTS_PER_REG;
29053bd332aSSY Chiu 	uint32_t mask = 1 << (it % NUM_INTS_PER_REG);
291b0104773SPascal Brand 
292*67e55c51SEtienne Carriere 	assert(gd == &gic_data);
293*67e55c51SEtienne Carriere 
294b0104773SPascal Brand 	/* Disable the interrupt */
295918bb3a5SEtienne Carriere 	io_write32(gd->gicd_base + GICD_ICENABLER(idx), mask);
296b0104773SPascal Brand 	/* Make it non-pending */
297918bb3a5SEtienne Carriere 	io_write32(gd->gicd_base + GICD_ICPENDR(idx), mask);
298b0104773SPascal Brand 	/* Assign it to group0 */
299918bb3a5SEtienne Carriere 	io_clrbits32(gd->gicd_base + GICD_IGROUPR(idx), mask);
3001fcac774SSandeep Tripathy #if defined(CFG_ARM_GICV3)
3011fcac774SSandeep Tripathy 	/* Assign it to group1S */
3021fcac774SSandeep Tripathy 	io_setbits32(gd->gicd_base + GICD_IGROUPMODR(idx), mask);
3031fcac774SSandeep Tripathy #endif
304b0104773SPascal Brand }
305b0104773SPascal Brand 
3067315b7b4SJens Wiklander static void gic_it_set_cpu_mask(struct gic_data *gd, size_t it,
3077315b7b4SJens Wiklander 				uint8_t cpu_mask)
308b0104773SPascal Brand {
3098ddf5a4eSEtienne Carriere 	size_t idx __maybe_unused = it / NUM_INTS_PER_REG;
3108ddf5a4eSEtienne Carriere 	uint32_t mask __maybe_unused = 1 << (it % NUM_INTS_PER_REG);
31153bd332aSSY Chiu 	uint32_t target, target_shift;
312918bb3a5SEtienne Carriere 	vaddr_t itargetsr = gd->gicd_base +
313918bb3a5SEtienne Carriere 			    GICD_ITARGETSR(it / NUM_TARGETS_PER_REG);
314b0104773SPascal Brand 
315*67e55c51SEtienne Carriere 	assert(gd == &gic_data);
316*67e55c51SEtienne Carriere 
317b0104773SPascal Brand 	/* Assigned to group0 */
318918bb3a5SEtienne Carriere 	assert(!(io_read32(gd->gicd_base + GICD_IGROUPR(idx)) & mask));
319b0104773SPascal Brand 
320b0104773SPascal Brand 	/* Route it to selected CPUs */
321918bb3a5SEtienne Carriere 	target = io_read32(itargetsr);
32253bd332aSSY Chiu 	target_shift = (it % NUM_TARGETS_PER_REG) * ITARGETSR_FIELD_BITS;
32353bd332aSSY Chiu 	target &= ~(ITARGETSR_FIELD_MASK << target_shift);
32453bd332aSSY Chiu 	target |= cpu_mask << target_shift;
325918bb3a5SEtienne Carriere 	DMSG("cpu_mask: writing 0x%x to 0x%" PRIxVA, target, itargetsr);
326918bb3a5SEtienne Carriere 	io_write32(itargetsr, target);
327918bb3a5SEtienne Carriere 	DMSG("cpu_mask: 0x%x", io_read32(itargetsr));
328b0104773SPascal Brand }
329b0104773SPascal Brand 
3307315b7b4SJens Wiklander static void gic_it_set_prio(struct gic_data *gd, size_t it, uint8_t prio)
331b0104773SPascal Brand {
3328ddf5a4eSEtienne Carriere 	size_t idx __maybe_unused = it / NUM_INTS_PER_REG;
3338ddf5a4eSEtienne Carriere 	uint32_t mask __maybe_unused = 1 << (it % NUM_INTS_PER_REG);
334b0104773SPascal Brand 
335*67e55c51SEtienne Carriere 	assert(gd == &gic_data);
336*67e55c51SEtienne Carriere 
337b0104773SPascal Brand 	/* Assigned to group0 */
338918bb3a5SEtienne Carriere 	assert(!(io_read32(gd->gicd_base + GICD_IGROUPR(idx)) & mask));
339b0104773SPascal Brand 
340b0104773SPascal Brand 	/* Set prio it to selected CPUs */
3411f60363aSJens Wiklander 	DMSG("prio: writing 0x%x to 0x%" PRIxVA,
3427315b7b4SJens Wiklander 		prio, gd->gicd_base + GICD_IPRIORITYR(0) + it);
343918bb3a5SEtienne Carriere 	io_write8(gd->gicd_base + GICD_IPRIORITYR(0) + it, prio);
344b0104773SPascal Brand }
345b0104773SPascal Brand 
3467315b7b4SJens Wiklander static void gic_it_enable(struct gic_data *gd, size_t it)
347b0104773SPascal Brand {
34853bd332aSSY Chiu 	size_t idx = it / NUM_INTS_PER_REG;
34953bd332aSSY Chiu 	uint32_t mask = 1 << (it % NUM_INTS_PER_REG);
350918bb3a5SEtienne Carriere 	vaddr_t base = gd->gicd_base;
351b0104773SPascal Brand 
352*67e55c51SEtienne Carriere 	assert(gd == &gic_data);
353*67e55c51SEtienne Carriere 
354b0104773SPascal Brand 	/* Assigned to group0 */
355918bb3a5SEtienne Carriere 	assert(!(io_read32(base + GICD_IGROUPR(idx)) & mask));
356b0104773SPascal Brand 
357b0104773SPascal Brand 	/* Enable the interrupt */
358918bb3a5SEtienne Carriere 	io_write32(base + GICD_ISENABLER(idx), mask);
359b0104773SPascal Brand }
360b0104773SPascal Brand 
3617315b7b4SJens Wiklander static void gic_it_disable(struct gic_data *gd, size_t it)
362b0104773SPascal Brand {
36353bd332aSSY Chiu 	size_t idx = it / NUM_INTS_PER_REG;
36453bd332aSSY Chiu 	uint32_t mask = 1 << (it % NUM_INTS_PER_REG);
365b0104773SPascal Brand 
366*67e55c51SEtienne Carriere 	assert(gd == &gic_data);
367*67e55c51SEtienne Carriere 
368b0104773SPascal Brand 	/* Assigned to group0 */
369918bb3a5SEtienne Carriere 	assert(!(io_read32(gd->gicd_base + GICD_IGROUPR(idx)) & mask));
370b0104773SPascal Brand 
371b0104773SPascal Brand 	/* Disable the interrupt */
372918bb3a5SEtienne Carriere 	io_write32(gd->gicd_base + GICD_ICENABLER(idx), mask);
373b0104773SPascal Brand }
374b0104773SPascal Brand 
37526ed70ecSGuanchao Liang static void gic_it_set_pending(struct gic_data *gd, size_t it)
37626ed70ecSGuanchao Liang {
37726ed70ecSGuanchao Liang 	size_t idx = it / NUM_INTS_PER_REG;
37826ed70ecSGuanchao Liang 	uint32_t mask = BIT32(it % NUM_INTS_PER_REG);
37926ed70ecSGuanchao Liang 
380*67e55c51SEtienne Carriere 	assert(gd == &gic_data);
381*67e55c51SEtienne Carriere 
38226ed70ecSGuanchao Liang 	/* Should be Peripheral Interrupt */
38326ed70ecSGuanchao Liang 	assert(it >= NUM_SGI);
38426ed70ecSGuanchao Liang 
38526ed70ecSGuanchao Liang 	/* Raise the interrupt */
386918bb3a5SEtienne Carriere 	io_write32(gd->gicd_base + GICD_ISPENDR(idx), mask);
38726ed70ecSGuanchao Liang }
38826ed70ecSGuanchao Liang 
38926ed70ecSGuanchao Liang static void gic_it_raise_sgi(struct gic_data *gd, size_t it,
39026ed70ecSGuanchao Liang 		uint8_t cpu_mask, uint8_t group)
39126ed70ecSGuanchao Liang {
39226ed70ecSGuanchao Liang 	uint32_t mask_id = it & 0xf;
39326ed70ecSGuanchao Liang 	uint32_t mask_group = group & 0x1;
39426ed70ecSGuanchao Liang 	uint32_t mask_cpu = cpu_mask & 0xff;
39526ed70ecSGuanchao Liang 	uint32_t mask = (mask_id | SHIFT_U32(mask_group, 15) |
39626ed70ecSGuanchao Liang 		SHIFT_U32(mask_cpu, 16));
39726ed70ecSGuanchao Liang 
398*67e55c51SEtienne Carriere 	assert(gd == &gic_data);
399*67e55c51SEtienne Carriere 
40026ed70ecSGuanchao Liang 	/* Should be Software Generated Interrupt */
40126ed70ecSGuanchao Liang 	assert(it < NUM_SGI);
40226ed70ecSGuanchao Liang 
40326ed70ecSGuanchao Liang 	/* Raise the interrupt */
404918bb3a5SEtienne Carriere 	io_write32(gd->gicd_base + GICD_SGIR, mask);
40526ed70ecSGuanchao Liang }
40626ed70ecSGuanchao Liang 
40718901324SDavid Wang static uint32_t gic_read_iar(struct gic_data *gd __maybe_unused)
408b0104773SPascal Brand {
409*67e55c51SEtienne Carriere 	assert(gd == &gic_data);
410*67e55c51SEtienne Carriere 
41118901324SDavid Wang #if defined(CFG_ARM_GICV3)
4121de462e1SSumit Garg 	return read_icc_iar1();
41318901324SDavid Wang #else
414918bb3a5SEtienne Carriere 	return io_read32(gd->gicc_base + GICC_IAR);
41518901324SDavid Wang #endif
416b0104773SPascal Brand }
417b0104773SPascal Brand 
41818901324SDavid Wang static void gic_write_eoir(struct gic_data *gd __maybe_unused, uint32_t eoir)
419b0104773SPascal Brand {
420*67e55c51SEtienne Carriere 	assert(gd == &gic_data);
421*67e55c51SEtienne Carriere 
42218901324SDavid Wang #if defined(CFG_ARM_GICV3)
4231de462e1SSumit Garg 	write_icc_eoir1(eoir);
42418901324SDavid Wang #else
425918bb3a5SEtienne Carriere 	io_write32(gd->gicc_base + GICC_EOIR, eoir);
42618901324SDavid Wang #endif
427b0104773SPascal Brand }
428b0104773SPascal Brand 
4297315b7b4SJens Wiklander static bool gic_it_is_enabled(struct gic_data *gd, size_t it)
4307315b7b4SJens Wiklander {
43153bd332aSSY Chiu 	size_t idx = it / NUM_INTS_PER_REG;
43253bd332aSSY Chiu 	uint32_t mask = 1 << (it % NUM_INTS_PER_REG);
433*67e55c51SEtienne Carriere 
434*67e55c51SEtienne Carriere 	assert(gd == &gic_data);
435918bb3a5SEtienne Carriere 	return !!(io_read32(gd->gicd_base + GICD_ISENABLER(idx)) & mask);
43653bd332aSSY Chiu }
43753bd332aSSY Chiu 
4387315b7b4SJens Wiklander static bool __maybe_unused gic_it_get_group(struct gic_data *gd, size_t it)
4397315b7b4SJens Wiklander {
44053bd332aSSY Chiu 	size_t idx = it / NUM_INTS_PER_REG;
44153bd332aSSY Chiu 	uint32_t mask = 1 << (it % NUM_INTS_PER_REG);
442*67e55c51SEtienne Carriere 
443*67e55c51SEtienne Carriere 	assert(gd == &gic_data);
444918bb3a5SEtienne Carriere 	return !!(io_read32(gd->gicd_base + GICD_IGROUPR(idx)) & mask);
44553bd332aSSY Chiu }
44653bd332aSSY Chiu 
4477315b7b4SJens Wiklander static uint32_t __maybe_unused gic_it_get_target(struct gic_data *gd, size_t it)
4487315b7b4SJens Wiklander {
44953bd332aSSY Chiu 	size_t reg_idx = it / NUM_TARGETS_PER_REG;
4507315b7b4SJens Wiklander 	uint32_t target_shift = (it % NUM_TARGETS_PER_REG) *
4517315b7b4SJens Wiklander 				ITARGETSR_FIELD_BITS;
45253bd332aSSY Chiu 	uint32_t target_mask = ITARGETSR_FIELD_MASK << target_shift;
453918bb3a5SEtienne Carriere 	uint32_t target = io_read32(gd->gicd_base + GICD_ITARGETSR(reg_idx));
4547315b7b4SJens Wiklander 
455*67e55c51SEtienne Carriere 	assert(gd == &gic_data);
456918bb3a5SEtienne Carriere 	return (target & target_mask) >> target_shift;
45753bd332aSSY Chiu }
45853bd332aSSY Chiu 
459*67e55c51SEtienne Carriere void gic_dump_state(void)
46053bd332aSSY Chiu {
461*67e55c51SEtienne Carriere 	struct gic_data *gd = &gic_data;
462*67e55c51SEtienne Carriere 	int i = 0;
46353bd332aSSY Chiu 
46418901324SDavid Wang #if defined(CFG_ARM_GICV3)
46518901324SDavid Wang 	DMSG("GICC_CTLR: 0x%x", read_icc_ctlr());
46618901324SDavid Wang #else
467918bb3a5SEtienne Carriere 	DMSG("GICC_CTLR: 0x%x", io_read32(gd->gicc_base + GICC_CTLR));
46818901324SDavid Wang #endif
469918bb3a5SEtienne Carriere 	DMSG("GICD_CTLR: 0x%x", io_read32(gd->gicd_base + GICD_CTLR));
4707315b7b4SJens Wiklander 
4714a9ea08cSFangsuo Wu 	for (i = 0; i <= (int)gd->max_it; i++) {
4727315b7b4SJens Wiklander 		if (gic_it_is_enabled(gd, i)) {
47353bd332aSSY Chiu 			DMSG("irq%d: enabled, group:%d, target:%x", i,
4747315b7b4SJens Wiklander 			     gic_it_get_group(gd, i), gic_it_get_target(gd, i));
47553bd332aSSY Chiu 		}
47653bd332aSSY Chiu 	}
47753bd332aSSY Chiu }
4787315b7b4SJens Wiklander 
479*67e55c51SEtienne Carriere static void __maybe_unused gic_native_itr_handler(void)
4807315b7b4SJens Wiklander {
481*67e55c51SEtienne Carriere 	struct gic_data *gd = &gic_data;
482*67e55c51SEtienne Carriere 	uint32_t iar = 0;
483*67e55c51SEtienne Carriere 	uint32_t id = 0;
4847315b7b4SJens Wiklander 
4857315b7b4SJens Wiklander 	iar = gic_read_iar(gd);
4867315b7b4SJens Wiklander 	id = iar & GICC_IAR_IT_ID_MASK;
4877315b7b4SJens Wiklander 
4884a9ea08cSFangsuo Wu 	if (id <= gd->max_it)
4897315b7b4SJens Wiklander 		itr_handle(id);
4903b3a4611SMathieu Briand 	else
4913b3a4611SMathieu Briand 		DMSG("ignoring interrupt %" PRIu32, id);
4927315b7b4SJens Wiklander 
4937315b7b4SJens Wiklander 	gic_write_eoir(gd, iar);
4947315b7b4SJens Wiklander }
4957315b7b4SJens Wiklander 
496*67e55c51SEtienne Carriere #ifndef CFG_CORE_WORKAROUND_ARM_NMFI
497*67e55c51SEtienne Carriere /* Override itr_core_handler() with core interrupt controller implementation */
498*67e55c51SEtienne Carriere void itr_core_handler(void)
499*67e55c51SEtienne Carriere {
500*67e55c51SEtienne Carriere 	gic_native_itr_handler();
501*67e55c51SEtienne Carriere }
502*67e55c51SEtienne Carriere #endif /*CFG_CORE_WORKAROUND_ARM_NMFI*/
503*67e55c51SEtienne Carriere 
5047315b7b4SJens Wiklander static void gic_op_add(struct itr_chip *chip, size_t it,
505702fe5a7SClément Léger 		       uint32_t type __unused,
506702fe5a7SClément Léger 		       uint32_t prio __unused)
5077315b7b4SJens Wiklander {
5087315b7b4SJens Wiklander 	struct gic_data *gd = container_of(chip, struct gic_data, chip);
5097315b7b4SJens Wiklander 
510*67e55c51SEtienne Carriere 	assert(gd == &gic_data);
511*67e55c51SEtienne Carriere 
5124a9ea08cSFangsuo Wu 	if (it > gd->max_it)
513d13278b8SEtienne Carriere 		panic();
514d13278b8SEtienne Carriere 
5157315b7b4SJens Wiklander 	gic_it_add(gd, it);
5167315b7b4SJens Wiklander 	/* Set the CPU mask to deliver interrupts to any online core */
5177315b7b4SJens Wiklander 	gic_it_set_cpu_mask(gd, it, 0xff);
5187315b7b4SJens Wiklander 	gic_it_set_prio(gd, it, 0x1);
5197315b7b4SJens Wiklander }
5207315b7b4SJens Wiklander 
5217315b7b4SJens Wiklander static void gic_op_enable(struct itr_chip *chip, size_t it)
5227315b7b4SJens Wiklander {
5237315b7b4SJens Wiklander 	struct gic_data *gd = container_of(chip, struct gic_data, chip);
5247315b7b4SJens Wiklander 
525*67e55c51SEtienne Carriere 	assert(gd == &gic_data);
526*67e55c51SEtienne Carriere 
5274a9ea08cSFangsuo Wu 	if (it > gd->max_it)
528d13278b8SEtienne Carriere 		panic();
529d13278b8SEtienne Carriere 
5307315b7b4SJens Wiklander 	gic_it_enable(gd, it);
5317315b7b4SJens Wiklander }
5327315b7b4SJens Wiklander 
5337315b7b4SJens Wiklander static void gic_op_disable(struct itr_chip *chip, size_t it)
5347315b7b4SJens Wiklander {
5357315b7b4SJens Wiklander 	struct gic_data *gd = container_of(chip, struct gic_data, chip);
5367315b7b4SJens Wiklander 
537*67e55c51SEtienne Carriere 	assert(gd == &gic_data);
538*67e55c51SEtienne Carriere 
5394a9ea08cSFangsuo Wu 	if (it > gd->max_it)
540d13278b8SEtienne Carriere 		panic();
541d13278b8SEtienne Carriere 
5427315b7b4SJens Wiklander 	gic_it_disable(gd, it);
5437315b7b4SJens Wiklander }
54426ed70ecSGuanchao Liang 
54526ed70ecSGuanchao Liang static void gic_op_raise_pi(struct itr_chip *chip, size_t it)
54626ed70ecSGuanchao Liang {
54726ed70ecSGuanchao Liang 	struct gic_data *gd = container_of(chip, struct gic_data, chip);
54826ed70ecSGuanchao Liang 
549*67e55c51SEtienne Carriere 	assert(gd == &gic_data);
550*67e55c51SEtienne Carriere 
5514a9ea08cSFangsuo Wu 	if (it > gd->max_it)
55226ed70ecSGuanchao Liang 		panic();
55326ed70ecSGuanchao Liang 
55426ed70ecSGuanchao Liang 	gic_it_set_pending(gd, it);
55526ed70ecSGuanchao Liang }
55626ed70ecSGuanchao Liang 
55726ed70ecSGuanchao Liang static void gic_op_raise_sgi(struct itr_chip *chip, size_t it,
55826ed70ecSGuanchao Liang 			uint8_t cpu_mask)
55926ed70ecSGuanchao Liang {
56026ed70ecSGuanchao Liang 	struct gic_data *gd = container_of(chip, struct gic_data, chip);
56126ed70ecSGuanchao Liang 
562*67e55c51SEtienne Carriere 	assert(gd == &gic_data);
563*67e55c51SEtienne Carriere 
5644a9ea08cSFangsuo Wu 	if (it > gd->max_it)
56526ed70ecSGuanchao Liang 		panic();
56626ed70ecSGuanchao Liang 
56726ed70ecSGuanchao Liang 	if (it < NUM_NS_SGI)
56826ed70ecSGuanchao Liang 		gic_it_raise_sgi(gd, it, cpu_mask, 1);
56926ed70ecSGuanchao Liang 	else
57026ed70ecSGuanchao Liang 		gic_it_raise_sgi(gd, it, cpu_mask, 0);
57126ed70ecSGuanchao Liang }
572*67e55c51SEtienne Carriere 
57326ed70ecSGuanchao Liang static void gic_op_set_affinity(struct itr_chip *chip, size_t it,
57426ed70ecSGuanchao Liang 			uint8_t cpu_mask)
57526ed70ecSGuanchao Liang {
57626ed70ecSGuanchao Liang 	struct gic_data *gd = container_of(chip, struct gic_data, chip);
57726ed70ecSGuanchao Liang 
578*67e55c51SEtienne Carriere 	assert(gd == &gic_data);
579*67e55c51SEtienne Carriere 
5804a9ea08cSFangsuo Wu 	if (it > gd->max_it)
58126ed70ecSGuanchao Liang 		panic();
58226ed70ecSGuanchao Liang 
58326ed70ecSGuanchao Liang 	gic_it_set_cpu_mask(gd, it, cpu_mask);
58426ed70ecSGuanchao Liang }
585