xref: /optee_os/core/drivers/gic.c (revision ec740b9fe95ef2c24769caeaf0f3cb32ec13c84c)
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 
691b4c5002SIzhar Nevo #define GICD_TYPER_IT_LINES_NUM_MASK	0x1f
707315b7b4SJens Wiklander #define GICC_IAR_IT_ID_MASK	0x3ff
717315b7b4SJens Wiklander #define GICC_IAR_CPU_ID_MASK	0x7
727315b7b4SJens Wiklander #define GICC_IAR_CPU_ID_SHIFT	10
73b0104773SPascal Brand 
74*ec740b9fSJens Wiklander #define GICC_SGI_IRM_BIT	40
75*ec740b9fSJens Wiklander #define GICC_SGI_AFF1_SHIFT	16
76*ec740b9fSJens Wiklander #define GICC_SGI_AFF2_SHIFT	32
77*ec740b9fSJens Wiklander #define GICC_SGI_AFF3_SHIFT	48
78*ec740b9fSJens Wiklander 
79*ec740b9fSJens Wiklander #define GICD_SGIR_SIGINTID_MASK			0xf
80*ec740b9fSJens Wiklander #define GICD_SGIR_TO_OTHER_CPUS			0x1
81*ec740b9fSJens Wiklander #define GICD_SGIR_TO_THIS_CPU			0x2
82*ec740b9fSJens Wiklander #define GICD_SGIR_TARGET_LIST_FILTER_SHIFT	24
83*ec740b9fSJens Wiklander #define GICD_SGIR_NSATT_SHIFT			15
84*ec740b9fSJens Wiklander #define GICD_SGIR_CPU_TARGET_LIST_SHIFT		16
85*ec740b9fSJens Wiklander 
8667e55c51SEtienne Carriere struct gic_data {
8767e55c51SEtienne Carriere 	vaddr_t gicc_base;
8867e55c51SEtienne Carriere 	vaddr_t gicd_base;
8967e55c51SEtienne Carriere 	size_t max_it;
9067e55c51SEtienne Carriere 	struct itr_chip chip;
9167e55c51SEtienne Carriere };
9267e55c51SEtienne Carriere 
9367e55c51SEtienne Carriere static struct gic_data gic_data __nex_bss;
9467e55c51SEtienne Carriere 
95702fe5a7SClément Léger static void gic_op_add(struct itr_chip *chip, size_t it, uint32_t type,
96702fe5a7SClément Léger 		       uint32_t prio);
977315b7b4SJens Wiklander static void gic_op_enable(struct itr_chip *chip, size_t it);
987315b7b4SJens Wiklander static void gic_op_disable(struct itr_chip *chip, size_t it);
9926ed70ecSGuanchao Liang static void gic_op_raise_pi(struct itr_chip *chip, size_t it);
10026ed70ecSGuanchao Liang static void gic_op_raise_sgi(struct itr_chip *chip, size_t it,
101*ec740b9fSJens Wiklander 			     uint32_t cpu_mask);
10226ed70ecSGuanchao Liang static void gic_op_set_affinity(struct itr_chip *chip, size_t it,
10326ed70ecSGuanchao Liang 			uint8_t cpu_mask);
1047315b7b4SJens Wiklander 
1057315b7b4SJens Wiklander static const struct itr_ops gic_ops = {
1067315b7b4SJens Wiklander 	.add = gic_op_add,
10708ded0e1SEtienne Carriere 	.mask = gic_op_disable,
10808ded0e1SEtienne Carriere 	.unmask = gic_op_enable,
1097315b7b4SJens Wiklander 	.enable = gic_op_enable,
1107315b7b4SJens Wiklander 	.disable = gic_op_disable,
11126ed70ecSGuanchao Liang 	.raise_pi = gic_op_raise_pi,
11226ed70ecSGuanchao Liang 	.raise_sgi = gic_op_raise_sgi,
11326ed70ecSGuanchao Liang 	.set_affinity = gic_op_set_affinity,
1147315b7b4SJens Wiklander };
1153639b55fSJerome Forissier DECLARE_KEEP_PAGER(gic_ops);
1167315b7b4SJens Wiklander 
11718901324SDavid Wang static size_t probe_max_it(vaddr_t gicc_base __maybe_unused, vaddr_t gicd_base)
118b0104773SPascal Brand {
119b0104773SPascal Brand 	int i;
120b0104773SPascal Brand 	uint32_t old_ctlr;
121b0104773SPascal Brand 	size_t ret = 0;
1221b4c5002SIzhar Nevo 	size_t max_regs = io_read32(gicd_base + GICD_TYPER) &
1231b4c5002SIzhar Nevo 			  GICD_TYPER_IT_LINES_NUM_MASK;
124b0104773SPascal Brand 
125b0104773SPascal Brand 	/*
126b0104773SPascal Brand 	 * Probe which interrupt number is the largest.
127b0104773SPascal Brand 	 */
12818901324SDavid Wang #if defined(CFG_ARM_GICV3)
12918901324SDavid Wang 	old_ctlr = read_icc_ctlr();
13018901324SDavid Wang 	write_icc_ctlr(0);
13118901324SDavid Wang #else
132918bb3a5SEtienne Carriere 	old_ctlr = io_read32(gicc_base + GICC_CTLR);
133918bb3a5SEtienne Carriere 	io_write32(gicc_base + GICC_CTLR, 0);
13418901324SDavid Wang #endif
13579f008d3SJens Wiklander 	for (i = max_regs; i >= 0; i--) {
136b0104773SPascal Brand 		uint32_t old_reg;
137b0104773SPascal Brand 		uint32_t reg;
138b0104773SPascal Brand 		int b;
139b0104773SPascal Brand 
140918bb3a5SEtienne Carriere 		old_reg = io_read32(gicd_base + GICD_ISENABLER(i));
141918bb3a5SEtienne Carriere 		io_write32(gicd_base + GICD_ISENABLER(i), 0xffffffff);
142918bb3a5SEtienne Carriere 		reg = io_read32(gicd_base + GICD_ISENABLER(i));
143918bb3a5SEtienne Carriere 		io_write32(gicd_base + GICD_ICENABLER(i), ~old_reg);
14479f008d3SJens Wiklander 		for (b = NUM_INTS_PER_REG - 1; b >= 0; b--) {
145007a97a2SJens Wiklander 			if (BIT32(b) & reg) {
14653bd332aSSY Chiu 				ret = i * NUM_INTS_PER_REG + b;
147b0104773SPascal Brand 				goto out;
148b0104773SPascal Brand 			}
149b0104773SPascal Brand 		}
150b0104773SPascal Brand 	}
151b0104773SPascal Brand out:
15218901324SDavid Wang #if defined(CFG_ARM_GICV3)
15318901324SDavid Wang 	write_icc_ctlr(old_ctlr);
15418901324SDavid Wang #else
155918bb3a5SEtienne Carriere 	io_write32(gicc_base + GICC_CTLR, old_ctlr);
15618901324SDavid Wang #endif
157b0104773SPascal Brand 	return ret;
158b0104773SPascal Brand }
159b0104773SPascal Brand 
16067e55c51SEtienne Carriere void gic_cpu_init(void)
161bedc2b9fSsunny {
16267e55c51SEtienne Carriere 	struct gic_data *gd = &gic_data;
16367e55c51SEtienne Carriere 
16418901324SDavid Wang #if defined(CFG_ARM_GICV3)
16518901324SDavid Wang 	assert(gd->gicd_base);
16618901324SDavid Wang #else
16705efe1e1SEtienne Carriere 	assert(gd->gicd_base && gd->gicc_base);
16818901324SDavid Wang #endif
16905efe1e1SEtienne Carriere 
170e06e6e74SPeter Maydell 	/* per-CPU interrupts config:
171bedc2b9fSsunny 	 * ID0-ID7(SGI)   for Non-secure interrupts
172bedc2b9fSsunny 	 * ID8-ID15(SGI)  for Secure interrupts.
173bedc2b9fSsunny 	 * All PPI config as Non-secure interrupts.
174bedc2b9fSsunny 	 */
175918bb3a5SEtienne Carriere 	io_write32(gd->gicd_base + GICD_IGROUPR(0), 0xffff00ff);
176bedc2b9fSsunny 
17730a673e3SPeter Maydell 	/* Set the priority mask to permit Non-secure interrupts, and to
17830a673e3SPeter Maydell 	 * allow the Non-secure world to adjust the priority mask itself
17930a673e3SPeter Maydell 	 */
18018901324SDavid Wang #if defined(CFG_ARM_GICV3)
18118901324SDavid Wang 	write_icc_pmr(0x80);
1821fcac774SSandeep Tripathy 	write_icc_igrpen1(1);
18318901324SDavid Wang #else
184918bb3a5SEtienne Carriere 	io_write32(gd->gicc_base + GICC_PMR, 0x80);
18530a673e3SPeter Maydell 
186bedc2b9fSsunny 	/* Enable GIC */
187918bb3a5SEtienne Carriere 	io_write32(gd->gicc_base + GICC_CTLR,
188918bb3a5SEtienne Carriere 		   GICC_CTLR_ENABLEGRP0 | GICC_CTLR_ENABLEGRP1 |
189918bb3a5SEtienne Carriere 		   GICC_CTLR_FIQEN);
19018901324SDavid Wang #endif
191bedc2b9fSsunny }
192bedc2b9fSsunny 
1930ee3f52eSEtienne Carriere static int gic_dt_get_irq(const uint32_t *properties, int count, uint32_t *type,
1940ee3f52eSEtienne Carriere 			  uint32_t *prio)
1950ee3f52eSEtienne Carriere {
1960ee3f52eSEtienne Carriere 	int it_num = DT_INFO_INVALID_INTERRUPT;
1970ee3f52eSEtienne Carriere 
1980ee3f52eSEtienne Carriere 	if (type)
1990ee3f52eSEtienne Carriere 		*type = IRQ_TYPE_NONE;
2000ee3f52eSEtienne Carriere 
2010ee3f52eSEtienne Carriere 	if (prio)
2020ee3f52eSEtienne Carriere 		*prio = 0;
2030ee3f52eSEtienne Carriere 
2040ee3f52eSEtienne Carriere 	if (!properties || count < 2)
2050ee3f52eSEtienne Carriere 		return DT_INFO_INVALID_INTERRUPT;
2060ee3f52eSEtienne Carriere 
2070ee3f52eSEtienne Carriere 	it_num = fdt32_to_cpu(properties[1]);
2080ee3f52eSEtienne Carriere 
2090ee3f52eSEtienne Carriere 	switch (fdt32_to_cpu(properties[0])) {
2100ee3f52eSEtienne Carriere 	case 1:
2110ee3f52eSEtienne Carriere 		it_num += 16;
2120ee3f52eSEtienne Carriere 		break;
2130ee3f52eSEtienne Carriere 	case 0:
2140ee3f52eSEtienne Carriere 		it_num += 32;
2150ee3f52eSEtienne Carriere 		break;
2160ee3f52eSEtienne Carriere 	default:
2170ee3f52eSEtienne Carriere 		it_num = DT_INFO_INVALID_INTERRUPT;
2180ee3f52eSEtienne Carriere 	}
2190ee3f52eSEtienne Carriere 
2200ee3f52eSEtienne Carriere 	return it_num;
2210ee3f52eSEtienne Carriere }
2220ee3f52eSEtienne Carriere 
2230ee3f52eSEtienne Carriere static void gic_init_base_addr(paddr_t gicc_base_pa, paddr_t gicd_base_pa)
224b0104773SPascal Brand {
22567e55c51SEtienne Carriere 	struct gic_data *gd = &gic_data;
2260ee3f52eSEtienne Carriere 	vaddr_t gicc_base = 0;
2270ee3f52eSEtienne Carriere 	vaddr_t gicd_base = 0;
2280ee3f52eSEtienne Carriere 
2290ee3f52eSEtienne Carriere 	assert(cpu_mmu_enabled());
2300ee3f52eSEtienne Carriere 
2310ee3f52eSEtienne Carriere 	gicd_base = core_mmu_get_va(gicd_base_pa, MEM_AREA_IO_SEC,
2320ee3f52eSEtienne Carriere 				    GIC_DIST_REG_SIZE);
2330ee3f52eSEtienne Carriere 	if (!gicd_base)
2340ee3f52eSEtienne Carriere 		panic();
2350ee3f52eSEtienne Carriere 
2360ee3f52eSEtienne Carriere 	if (!IS_ENABLED(CFG_ARM_GICV3)) {
2370ee3f52eSEtienne Carriere 		gicc_base = core_mmu_get_va(gicc_base_pa, MEM_AREA_IO_SEC,
2380ee3f52eSEtienne Carriere 					    GIC_CPU_REG_SIZE);
2390ee3f52eSEtienne Carriere 		if (!gicc_base)
2400ee3f52eSEtienne Carriere 			panic();
2410ee3f52eSEtienne Carriere 	}
2420ee3f52eSEtienne Carriere 
2430ee3f52eSEtienne Carriere 	gd->gicc_base = gicc_base;
2440ee3f52eSEtienne Carriere 	gd->gicd_base = gicd_base;
2450ee3f52eSEtienne Carriere 	gd->max_it = probe_max_it(gicc_base, gicd_base);
2460ee3f52eSEtienne Carriere 	gd->chip.ops = &gic_ops;
2470ee3f52eSEtienne Carriere 
2480ee3f52eSEtienne Carriere 	if (IS_ENABLED(CFG_DT))
2490ee3f52eSEtienne Carriere 		gd->chip.dt_get_irq = gic_dt_get_irq;
2500ee3f52eSEtienne Carriere }
2510ee3f52eSEtienne Carriere 
2520ee3f52eSEtienne Carriere void gic_init(paddr_t gicc_base_pa, paddr_t gicd_base_pa)
2530ee3f52eSEtienne Carriere {
2540ee3f52eSEtienne Carriere 	struct gic_data __maybe_unused *gd = &gic_data;
2550ee3f52eSEtienne Carriere 	size_t __maybe_unused n = 0;
256b0104773SPascal Brand 
25767e55c51SEtienne Carriere 	gic_init_base_addr(gicc_base_pa, gicd_base_pa);
258b0104773SPascal Brand 
2590ee3f52eSEtienne Carriere 	/* GIC configuration is initialized from TF-A when embedded */
2600ee3f52eSEtienne Carriere #ifndef CFG_WITH_ARM_TRUSTED_FW
2617315b7b4SJens Wiklander 	for (n = 0; n <= gd->max_it / NUM_INTS_PER_REG; n++) {
262b0104773SPascal Brand 		/* Disable interrupts */
263918bb3a5SEtienne Carriere 		io_write32(gd->gicd_base + GICD_ICENABLER(n), 0xffffffff);
264b0104773SPascal Brand 
265b0104773SPascal Brand 		/* Make interrupts non-pending */
266918bb3a5SEtienne Carriere 		io_write32(gd->gicd_base + GICD_ICPENDR(n), 0xffffffff);
267b0104773SPascal Brand 
268b0104773SPascal Brand 		/* Mark interrupts non-secure */
269bedc2b9fSsunny 		if (n == 0) {
270bedc2b9fSsunny 			/* per-CPU inerrupts config:
271bedc2b9fSsunny 			 * ID0-ID7(SGI)	  for Non-secure interrupts
272bedc2b9fSsunny 			 * ID8-ID15(SGI)  for Secure interrupts.
273bedc2b9fSsunny 			 * All PPI config as Non-secure interrupts.
274bedc2b9fSsunny 			 */
275918bb3a5SEtienne Carriere 			io_write32(gd->gicd_base + GICD_IGROUPR(n), 0xffff00ff);
276bedc2b9fSsunny 		} else {
277918bb3a5SEtienne Carriere 			io_write32(gd->gicd_base + GICD_IGROUPR(n), 0xffffffff);
278b0104773SPascal Brand 		}
279bedc2b9fSsunny 	}
280b0104773SPascal Brand 
28130a673e3SPeter Maydell 	/* Set the priority mask to permit Non-secure interrupts, and to
28230a673e3SPeter Maydell 	 * allow the Non-secure world to adjust the priority mask itself
28330a673e3SPeter Maydell 	 */
28418901324SDavid Wang #if defined(CFG_ARM_GICV3)
28518901324SDavid Wang 	write_icc_pmr(0x80);
2861fcac774SSandeep Tripathy 	write_icc_igrpen1(1);
2871fcac774SSandeep Tripathy 	io_setbits32(gd->gicd_base + GICD_CTLR, GICD_CTLR_ENABLEGRP1S);
28818901324SDavid Wang #else
289918bb3a5SEtienne Carriere 	io_write32(gd->gicc_base + GICC_PMR, 0x80);
29030a673e3SPeter Maydell 
291b0104773SPascal Brand 	/* Enable GIC */
292918bb3a5SEtienne Carriere 	io_write32(gd->gicc_base + GICC_CTLR, GICC_CTLR_FIQEN |
293918bb3a5SEtienne Carriere 		   GICC_CTLR_ENABLEGRP0 | GICC_CTLR_ENABLEGRP1);
294918bb3a5SEtienne Carriere 	io_setbits32(gd->gicd_base + GICD_CTLR,
295918bb3a5SEtienne Carriere 		     GICD_CTLR_ENABLEGRP0 | GICD_CTLR_ENABLEGRP1);
2961fcac774SSandeep Tripathy #endif
2970ee3f52eSEtienne Carriere #endif /*CFG_WITH_ARM_TRUSTED_FW*/
29867e55c51SEtienne Carriere 
29901980f3fSEtienne Carriere 	interrupt_main_init(&gic_data.chip);
300b0104773SPascal Brand }
301b0104773SPascal Brand 
3027315b7b4SJens Wiklander static void gic_it_add(struct gic_data *gd, size_t it)
303b0104773SPascal Brand {
30453bd332aSSY Chiu 	size_t idx = it / NUM_INTS_PER_REG;
30553bd332aSSY Chiu 	uint32_t mask = 1 << (it % NUM_INTS_PER_REG);
306b0104773SPascal Brand 
30767e55c51SEtienne Carriere 	assert(gd == &gic_data);
30867e55c51SEtienne Carriere 
309b0104773SPascal Brand 	/* Disable the interrupt */
310918bb3a5SEtienne Carriere 	io_write32(gd->gicd_base + GICD_ICENABLER(idx), mask);
311b0104773SPascal Brand 	/* Make it non-pending */
312918bb3a5SEtienne Carriere 	io_write32(gd->gicd_base + GICD_ICPENDR(idx), mask);
313b0104773SPascal Brand 	/* Assign it to group0 */
314918bb3a5SEtienne Carriere 	io_clrbits32(gd->gicd_base + GICD_IGROUPR(idx), mask);
3151fcac774SSandeep Tripathy #if defined(CFG_ARM_GICV3)
3161fcac774SSandeep Tripathy 	/* Assign it to group1S */
3171fcac774SSandeep Tripathy 	io_setbits32(gd->gicd_base + GICD_IGROUPMODR(idx), mask);
3181fcac774SSandeep Tripathy #endif
319b0104773SPascal Brand }
320b0104773SPascal Brand 
3217315b7b4SJens Wiklander static void gic_it_set_cpu_mask(struct gic_data *gd, size_t it,
3227315b7b4SJens Wiklander 				uint8_t cpu_mask)
323b0104773SPascal Brand {
3248ddf5a4eSEtienne Carriere 	size_t idx __maybe_unused = it / NUM_INTS_PER_REG;
3258ddf5a4eSEtienne Carriere 	uint32_t mask __maybe_unused = 1 << (it % NUM_INTS_PER_REG);
32653bd332aSSY Chiu 	uint32_t target, target_shift;
327918bb3a5SEtienne Carriere 	vaddr_t itargetsr = gd->gicd_base +
328918bb3a5SEtienne Carriere 			    GICD_ITARGETSR(it / NUM_TARGETS_PER_REG);
329b0104773SPascal Brand 
33067e55c51SEtienne Carriere 	assert(gd == &gic_data);
33167e55c51SEtienne Carriere 
332b0104773SPascal Brand 	/* Assigned to group0 */
333918bb3a5SEtienne Carriere 	assert(!(io_read32(gd->gicd_base + GICD_IGROUPR(idx)) & mask));
334b0104773SPascal Brand 
335b0104773SPascal Brand 	/* Route it to selected CPUs */
336918bb3a5SEtienne Carriere 	target = io_read32(itargetsr);
33753bd332aSSY Chiu 	target_shift = (it % NUM_TARGETS_PER_REG) * ITARGETSR_FIELD_BITS;
33853bd332aSSY Chiu 	target &= ~(ITARGETSR_FIELD_MASK << target_shift);
33953bd332aSSY Chiu 	target |= cpu_mask << target_shift;
340918bb3a5SEtienne Carriere 	DMSG("cpu_mask: writing 0x%x to 0x%" PRIxVA, target, itargetsr);
341918bb3a5SEtienne Carriere 	io_write32(itargetsr, target);
342918bb3a5SEtienne Carriere 	DMSG("cpu_mask: 0x%x", io_read32(itargetsr));
343b0104773SPascal Brand }
344b0104773SPascal Brand 
3457315b7b4SJens Wiklander static void gic_it_set_prio(struct gic_data *gd, size_t it, uint8_t prio)
346b0104773SPascal Brand {
3478ddf5a4eSEtienne Carriere 	size_t idx __maybe_unused = it / NUM_INTS_PER_REG;
3488ddf5a4eSEtienne Carriere 	uint32_t mask __maybe_unused = 1 << (it % NUM_INTS_PER_REG);
349b0104773SPascal Brand 
35067e55c51SEtienne Carriere 	assert(gd == &gic_data);
35167e55c51SEtienne Carriere 
352b0104773SPascal Brand 	/* Assigned to group0 */
353918bb3a5SEtienne Carriere 	assert(!(io_read32(gd->gicd_base + GICD_IGROUPR(idx)) & mask));
354b0104773SPascal Brand 
355b0104773SPascal Brand 	/* Set prio it to selected CPUs */
3561f60363aSJens Wiklander 	DMSG("prio: writing 0x%x to 0x%" PRIxVA,
3577315b7b4SJens Wiklander 		prio, gd->gicd_base + GICD_IPRIORITYR(0) + it);
358918bb3a5SEtienne Carriere 	io_write8(gd->gicd_base + GICD_IPRIORITYR(0) + it, prio);
359b0104773SPascal Brand }
360b0104773SPascal Brand 
3617315b7b4SJens Wiklander static void gic_it_enable(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);
365918bb3a5SEtienne Carriere 	vaddr_t base = gd->gicd_base;
366b0104773SPascal Brand 
36767e55c51SEtienne Carriere 	assert(gd == &gic_data);
36867e55c51SEtienne Carriere 
369b0104773SPascal Brand 	/* Assigned to group0 */
370918bb3a5SEtienne Carriere 	assert(!(io_read32(base + GICD_IGROUPR(idx)) & mask));
371b0104773SPascal Brand 
372b0104773SPascal Brand 	/* Enable the interrupt */
373918bb3a5SEtienne Carriere 	io_write32(base + GICD_ISENABLER(idx), mask);
374b0104773SPascal Brand }
375b0104773SPascal Brand 
3767315b7b4SJens Wiklander static void gic_it_disable(struct gic_data *gd, size_t it)
377b0104773SPascal Brand {
37853bd332aSSY Chiu 	size_t idx = it / NUM_INTS_PER_REG;
37953bd332aSSY Chiu 	uint32_t mask = 1 << (it % NUM_INTS_PER_REG);
380b0104773SPascal Brand 
38167e55c51SEtienne Carriere 	assert(gd == &gic_data);
38267e55c51SEtienne Carriere 
383b0104773SPascal Brand 	/* Assigned to group0 */
384918bb3a5SEtienne Carriere 	assert(!(io_read32(gd->gicd_base + GICD_IGROUPR(idx)) & mask));
385b0104773SPascal Brand 
386b0104773SPascal Brand 	/* Disable the interrupt */
387918bb3a5SEtienne Carriere 	io_write32(gd->gicd_base + GICD_ICENABLER(idx), mask);
388b0104773SPascal Brand }
389b0104773SPascal Brand 
39026ed70ecSGuanchao Liang static void gic_it_set_pending(struct gic_data *gd, size_t it)
39126ed70ecSGuanchao Liang {
39226ed70ecSGuanchao Liang 	size_t idx = it / NUM_INTS_PER_REG;
39326ed70ecSGuanchao Liang 	uint32_t mask = BIT32(it % NUM_INTS_PER_REG);
39426ed70ecSGuanchao Liang 
39567e55c51SEtienne Carriere 	assert(gd == &gic_data);
39667e55c51SEtienne Carriere 
39726ed70ecSGuanchao Liang 	/* Should be Peripheral Interrupt */
39826ed70ecSGuanchao Liang 	assert(it >= NUM_SGI);
39926ed70ecSGuanchao Liang 
40026ed70ecSGuanchao Liang 	/* Raise the interrupt */
401918bb3a5SEtienne Carriere 	io_write32(gd->gicd_base + GICD_ISPENDR(idx), mask);
40226ed70ecSGuanchao Liang }
40326ed70ecSGuanchao Liang 
404*ec740b9fSJens Wiklander static void assert_cpu_mask_is_valid(uint32_t cpu_mask)
405*ec740b9fSJens Wiklander {
406*ec740b9fSJens Wiklander 	bool __maybe_unused to_others = cpu_mask & ITR_CPU_MASK_TO_OTHER_CPUS;
407*ec740b9fSJens Wiklander 	bool __maybe_unused to_current = cpu_mask & ITR_CPU_MASK_TO_THIS_CPU;
408*ec740b9fSJens Wiklander 	bool __maybe_unused to_list = cpu_mask & 0xff;
409*ec740b9fSJens Wiklander 
410*ec740b9fSJens Wiklander 	/* One and only one of the bit fields shall be non-zero */
411*ec740b9fSJens Wiklander 	assert(to_others + to_current + to_list == 1);
412*ec740b9fSJens Wiklander }
413*ec740b9fSJens Wiklander 
41454739cb4SMark-PK Tsai static void gic_it_raise_sgi(struct gic_data *gd __maybe_unused, size_t it,
415*ec740b9fSJens Wiklander 			     uint32_t cpu_mask, uint8_t group)
41626ed70ecSGuanchao Liang {
41754739cb4SMark-PK Tsai #if defined(CFG_ARM_GICV3)
41854739cb4SMark-PK Tsai 	uint32_t mask_id = it & 0xf;
419*ec740b9fSJens Wiklander 	uint64_t mask = SHIFT_U64(mask_id, 24);
420*ec740b9fSJens Wiklander 
421*ec740b9fSJens Wiklander 	assert_cpu_mask_is_valid(cpu_mask);
422*ec740b9fSJens Wiklander 
423*ec740b9fSJens Wiklander 	if (cpu_mask & ITR_CPU_MASK_TO_OTHER_CPUS) {
424*ec740b9fSJens Wiklander 		mask |= BIT64(GICC_SGI_IRM_BIT);
425*ec740b9fSJens Wiklander 	} else {
42654739cb4SMark-PK Tsai 		uint64_t mpidr = read_mpidr();
427*ec740b9fSJens Wiklander 		uint64_t mask_aff1 = (mpidr & MPIDR_AFF1_MASK) >>
428*ec740b9fSJens Wiklander 				     MPIDR_AFF1_SHIFT;
429*ec740b9fSJens Wiklander 		uint64_t mask_aff2 = (mpidr & MPIDR_AFF2_MASK) >>
430*ec740b9fSJens Wiklander 				     MPIDR_AFF2_SHIFT;
431*ec740b9fSJens Wiklander 		uint64_t mask_aff3 = (mpidr & MPIDR_AFF3_MASK) >>
432*ec740b9fSJens Wiklander 				     MPIDR_AFF3_SHIFT;
433*ec740b9fSJens Wiklander 
434*ec740b9fSJens Wiklander 		mask |= SHIFT_U64(mask_aff1, GICC_SGI_AFF1_SHIFT);
435*ec740b9fSJens Wiklander 		mask |= SHIFT_U64(mask_aff2, GICC_SGI_AFF2_SHIFT);
436*ec740b9fSJens Wiklander 		mask |= SHIFT_U64(mask_aff3, GICC_SGI_AFF3_SHIFT);
437*ec740b9fSJens Wiklander 
438*ec740b9fSJens Wiklander 		if (cpu_mask & ITR_CPU_MASK_TO_THIS_CPU) {
439*ec740b9fSJens Wiklander 			mask |= BIT32(mpidr & 0xf);
440*ec740b9fSJens Wiklander 		} else {
441*ec740b9fSJens Wiklander 			/*
442*ec740b9fSJens Wiklander 			 * Only support sending SGI to the cores in the
443*ec740b9fSJens Wiklander 			 * same cluster now.
444*ec740b9fSJens Wiklander 			 */
445*ec740b9fSJens Wiklander 			mask |= cpu_mask & 0xff;
446*ec740b9fSJens Wiklander 		}
447*ec740b9fSJens Wiklander 	}
44854739cb4SMark-PK Tsai 
44954739cb4SMark-PK Tsai 	/* Raise the interrupt */
45054739cb4SMark-PK Tsai 	if (group)
45154739cb4SMark-PK Tsai 		write_icc_asgi1r(mask);
45254739cb4SMark-PK Tsai 	else
45354739cb4SMark-PK Tsai 		write_icc_sgi1r(mask);
45454739cb4SMark-PK Tsai #else
455*ec740b9fSJens Wiklander 	uint32_t mask_id = it & GICD_SGIR_SIGINTID_MASK;
45626ed70ecSGuanchao Liang 	uint32_t mask_group = group & 0x1;
457*ec740b9fSJens Wiklander 	uint32_t mask = mask_id;
458*ec740b9fSJens Wiklander 
459*ec740b9fSJens Wiklander 	assert_cpu_mask_is_valid(cpu_mask);
460*ec740b9fSJens Wiklander 
461*ec740b9fSJens Wiklander 	mask |= SHIFT_U32(mask_group, GICD_SGIR_NSATT_SHIFT);
462*ec740b9fSJens Wiklander 	if (cpu_mask & ITR_CPU_MASK_TO_OTHER_CPUS) {
463*ec740b9fSJens Wiklander 		mask |= SHIFT_U32(GICD_SGIR_TO_OTHER_CPUS,
464*ec740b9fSJens Wiklander 				  GICD_SGIR_TARGET_LIST_FILTER_SHIFT);
465*ec740b9fSJens Wiklander 	} else if (cpu_mask & ITR_CPU_MASK_TO_THIS_CPU) {
466*ec740b9fSJens Wiklander 		mask |= SHIFT_U32(GICD_SGIR_TO_THIS_CPU,
467*ec740b9fSJens Wiklander 				  GICD_SGIR_TARGET_LIST_FILTER_SHIFT);
468*ec740b9fSJens Wiklander 	} else {
469*ec740b9fSJens Wiklander 		mask |= SHIFT_U32(cpu_mask & 0xff,
470*ec740b9fSJens Wiklander 				  GICD_SGIR_CPU_TARGET_LIST_SHIFT);
471*ec740b9fSJens Wiklander 	}
47226ed70ecSGuanchao Liang 
47326ed70ecSGuanchao Liang 	/* Raise the interrupt */
474918bb3a5SEtienne Carriere 	io_write32(gd->gicd_base + GICD_SGIR, mask);
47554739cb4SMark-PK Tsai #endif
47626ed70ecSGuanchao Liang }
47726ed70ecSGuanchao Liang 
47818901324SDavid Wang static uint32_t gic_read_iar(struct gic_data *gd __maybe_unused)
479b0104773SPascal Brand {
48067e55c51SEtienne Carriere 	assert(gd == &gic_data);
48167e55c51SEtienne Carriere 
48218901324SDavid Wang #if defined(CFG_ARM_GICV3)
4831de462e1SSumit Garg 	return read_icc_iar1();
48418901324SDavid Wang #else
485918bb3a5SEtienne Carriere 	return io_read32(gd->gicc_base + GICC_IAR);
48618901324SDavid Wang #endif
487b0104773SPascal Brand }
488b0104773SPascal Brand 
48918901324SDavid Wang static void gic_write_eoir(struct gic_data *gd __maybe_unused, uint32_t eoir)
490b0104773SPascal Brand {
49167e55c51SEtienne Carriere 	assert(gd == &gic_data);
49267e55c51SEtienne Carriere 
49318901324SDavid Wang #if defined(CFG_ARM_GICV3)
4941de462e1SSumit Garg 	write_icc_eoir1(eoir);
49518901324SDavid Wang #else
496918bb3a5SEtienne Carriere 	io_write32(gd->gicc_base + GICC_EOIR, eoir);
49718901324SDavid Wang #endif
498b0104773SPascal Brand }
499b0104773SPascal Brand 
5007315b7b4SJens Wiklander static bool gic_it_is_enabled(struct gic_data *gd, size_t it)
5017315b7b4SJens Wiklander {
50253bd332aSSY Chiu 	size_t idx = it / NUM_INTS_PER_REG;
50353bd332aSSY Chiu 	uint32_t mask = 1 << (it % NUM_INTS_PER_REG);
50467e55c51SEtienne Carriere 
50567e55c51SEtienne Carriere 	assert(gd == &gic_data);
506918bb3a5SEtienne Carriere 	return !!(io_read32(gd->gicd_base + GICD_ISENABLER(idx)) & mask);
50753bd332aSSY Chiu }
50853bd332aSSY Chiu 
5097315b7b4SJens Wiklander static bool __maybe_unused gic_it_get_group(struct gic_data *gd, size_t it)
5107315b7b4SJens Wiklander {
51153bd332aSSY Chiu 	size_t idx = it / NUM_INTS_PER_REG;
51253bd332aSSY Chiu 	uint32_t mask = 1 << (it % NUM_INTS_PER_REG);
51367e55c51SEtienne Carriere 
51467e55c51SEtienne Carriere 	assert(gd == &gic_data);
515918bb3a5SEtienne Carriere 	return !!(io_read32(gd->gicd_base + GICD_IGROUPR(idx)) & mask);
51653bd332aSSY Chiu }
51753bd332aSSY Chiu 
5187315b7b4SJens Wiklander static uint32_t __maybe_unused gic_it_get_target(struct gic_data *gd, size_t it)
5197315b7b4SJens Wiklander {
52053bd332aSSY Chiu 	size_t reg_idx = it / NUM_TARGETS_PER_REG;
5217315b7b4SJens Wiklander 	uint32_t target_shift = (it % NUM_TARGETS_PER_REG) *
5227315b7b4SJens Wiklander 				ITARGETSR_FIELD_BITS;
52353bd332aSSY Chiu 	uint32_t target_mask = ITARGETSR_FIELD_MASK << target_shift;
524918bb3a5SEtienne Carriere 	uint32_t target = io_read32(gd->gicd_base + GICD_ITARGETSR(reg_idx));
5257315b7b4SJens Wiklander 
52667e55c51SEtienne Carriere 	assert(gd == &gic_data);
527918bb3a5SEtienne Carriere 	return (target & target_mask) >> target_shift;
52853bd332aSSY Chiu }
52953bd332aSSY Chiu 
53067e55c51SEtienne Carriere void gic_dump_state(void)
53153bd332aSSY Chiu {
53267e55c51SEtienne Carriere 	struct gic_data *gd = &gic_data;
53367e55c51SEtienne Carriere 	int i = 0;
53453bd332aSSY Chiu 
53518901324SDavid Wang #if defined(CFG_ARM_GICV3)
53618901324SDavid Wang 	DMSG("GICC_CTLR: 0x%x", read_icc_ctlr());
53718901324SDavid Wang #else
538918bb3a5SEtienne Carriere 	DMSG("GICC_CTLR: 0x%x", io_read32(gd->gicc_base + GICC_CTLR));
53918901324SDavid Wang #endif
540918bb3a5SEtienne Carriere 	DMSG("GICD_CTLR: 0x%x", io_read32(gd->gicd_base + GICD_CTLR));
5417315b7b4SJens Wiklander 
5424a9ea08cSFangsuo Wu 	for (i = 0; i <= (int)gd->max_it; i++) {
5437315b7b4SJens Wiklander 		if (gic_it_is_enabled(gd, i)) {
54453bd332aSSY Chiu 			DMSG("irq%d: enabled, group:%d, target:%x", i,
5457315b7b4SJens Wiklander 			     gic_it_get_group(gd, i), gic_it_get_target(gd, i));
54653bd332aSSY Chiu 		}
54753bd332aSSY Chiu 	}
54853bd332aSSY Chiu }
5497315b7b4SJens Wiklander 
55067e55c51SEtienne Carriere static void __maybe_unused gic_native_itr_handler(void)
5517315b7b4SJens Wiklander {
55267e55c51SEtienne Carriere 	struct gic_data *gd = &gic_data;
55367e55c51SEtienne Carriere 	uint32_t iar = 0;
55467e55c51SEtienne Carriere 	uint32_t id = 0;
5557315b7b4SJens Wiklander 
5567315b7b4SJens Wiklander 	iar = gic_read_iar(gd);
5577315b7b4SJens Wiklander 	id = iar & GICC_IAR_IT_ID_MASK;
5587315b7b4SJens Wiklander 
5594a9ea08cSFangsuo Wu 	if (id <= gd->max_it)
56099e2612cSEtienne Carriere 		interrupt_call_handlers(&gd->chip, id);
5613b3a4611SMathieu Briand 	else
5623b3a4611SMathieu Briand 		DMSG("ignoring interrupt %" PRIu32, id);
5637315b7b4SJens Wiklander 
5647315b7b4SJens Wiklander 	gic_write_eoir(gd, iar);
5657315b7b4SJens Wiklander }
5667315b7b4SJens Wiklander 
56767e55c51SEtienne Carriere #ifndef CFG_CORE_WORKAROUND_ARM_NMFI
568358bf47cSEtienne Carriere /* Override interrupt_main_handler() with driver implementation */
569358bf47cSEtienne Carriere void interrupt_main_handler(void)
57067e55c51SEtienne Carriere {
57167e55c51SEtienne Carriere 	gic_native_itr_handler();
57267e55c51SEtienne Carriere }
57367e55c51SEtienne Carriere #endif /*CFG_CORE_WORKAROUND_ARM_NMFI*/
57467e55c51SEtienne Carriere 
5757315b7b4SJens Wiklander static void gic_op_add(struct itr_chip *chip, size_t it,
576702fe5a7SClément Léger 		       uint32_t type __unused,
577702fe5a7SClément Léger 		       uint32_t prio __unused)
5787315b7b4SJens Wiklander {
5797315b7b4SJens Wiklander 	struct gic_data *gd = container_of(chip, struct gic_data, chip);
5807315b7b4SJens Wiklander 
58167e55c51SEtienne Carriere 	assert(gd == &gic_data);
58267e55c51SEtienne Carriere 
5834a9ea08cSFangsuo Wu 	if (it > gd->max_it)
584d13278b8SEtienne Carriere 		panic();
585d13278b8SEtienne Carriere 
5867315b7b4SJens Wiklander 	gic_it_add(gd, it);
5877315b7b4SJens Wiklander 	/* Set the CPU mask to deliver interrupts to any online core */
5887315b7b4SJens Wiklander 	gic_it_set_cpu_mask(gd, it, 0xff);
5897315b7b4SJens Wiklander 	gic_it_set_prio(gd, it, 0x1);
5907315b7b4SJens Wiklander }
5917315b7b4SJens Wiklander 
5927315b7b4SJens Wiklander static void gic_op_enable(struct itr_chip *chip, size_t it)
5937315b7b4SJens Wiklander {
5947315b7b4SJens Wiklander 	struct gic_data *gd = container_of(chip, struct gic_data, chip);
5957315b7b4SJens Wiklander 
59667e55c51SEtienne Carriere 	assert(gd == &gic_data);
59767e55c51SEtienne Carriere 
5984a9ea08cSFangsuo Wu 	if (it > gd->max_it)
599d13278b8SEtienne Carriere 		panic();
600d13278b8SEtienne Carriere 
6017315b7b4SJens Wiklander 	gic_it_enable(gd, it);
6027315b7b4SJens Wiklander }
6037315b7b4SJens Wiklander 
6047315b7b4SJens Wiklander static void gic_op_disable(struct itr_chip *chip, size_t it)
6057315b7b4SJens Wiklander {
6067315b7b4SJens Wiklander 	struct gic_data *gd = container_of(chip, struct gic_data, chip);
6077315b7b4SJens Wiklander 
60867e55c51SEtienne Carriere 	assert(gd == &gic_data);
60967e55c51SEtienne Carriere 
6104a9ea08cSFangsuo Wu 	if (it > gd->max_it)
611d13278b8SEtienne Carriere 		panic();
612d13278b8SEtienne Carriere 
6137315b7b4SJens Wiklander 	gic_it_disable(gd, it);
6147315b7b4SJens Wiklander }
61526ed70ecSGuanchao Liang 
61626ed70ecSGuanchao Liang static void gic_op_raise_pi(struct itr_chip *chip, size_t it)
61726ed70ecSGuanchao Liang {
61826ed70ecSGuanchao Liang 	struct gic_data *gd = container_of(chip, struct gic_data, chip);
61926ed70ecSGuanchao Liang 
62067e55c51SEtienne Carriere 	assert(gd == &gic_data);
62167e55c51SEtienne Carriere 
6224a9ea08cSFangsuo Wu 	if (it > gd->max_it)
62326ed70ecSGuanchao Liang 		panic();
62426ed70ecSGuanchao Liang 
62526ed70ecSGuanchao Liang 	gic_it_set_pending(gd, it);
62626ed70ecSGuanchao Liang }
62726ed70ecSGuanchao Liang 
62826ed70ecSGuanchao Liang static void gic_op_raise_sgi(struct itr_chip *chip, size_t it,
629*ec740b9fSJens Wiklander 			     uint32_t cpu_mask)
63026ed70ecSGuanchao Liang {
63126ed70ecSGuanchao Liang 	struct gic_data *gd = container_of(chip, struct gic_data, chip);
63226ed70ecSGuanchao Liang 
63367e55c51SEtienne Carriere 	assert(gd == &gic_data);
63467e55c51SEtienne Carriere 
63554739cb4SMark-PK Tsai 	/* Should be Software Generated Interrupt */
63654739cb4SMark-PK Tsai 	assert(it < NUM_SGI);
63754739cb4SMark-PK Tsai 
6384a9ea08cSFangsuo Wu 	if (it > gd->max_it)
63926ed70ecSGuanchao Liang 		panic();
64026ed70ecSGuanchao Liang 
64126ed70ecSGuanchao Liang 	if (it < NUM_NS_SGI)
64226ed70ecSGuanchao Liang 		gic_it_raise_sgi(gd, it, cpu_mask, 1);
64326ed70ecSGuanchao Liang 	else
64426ed70ecSGuanchao Liang 		gic_it_raise_sgi(gd, it, cpu_mask, 0);
64526ed70ecSGuanchao Liang }
64667e55c51SEtienne Carriere 
64726ed70ecSGuanchao Liang static void gic_op_set_affinity(struct itr_chip *chip, size_t it,
64826ed70ecSGuanchao Liang 			uint8_t cpu_mask)
64926ed70ecSGuanchao Liang {
65026ed70ecSGuanchao Liang 	struct gic_data *gd = container_of(chip, struct gic_data, chip);
65126ed70ecSGuanchao Liang 
65267e55c51SEtienne Carriere 	assert(gd == &gic_data);
65367e55c51SEtienne Carriere 
6544a9ea08cSFangsuo Wu 	if (it > gd->max_it)
65526ed70ecSGuanchao Liang 		panic();
65626ed70ecSGuanchao Liang 
65726ed70ecSGuanchao Liang 	gic_it_set_cpu_mask(gd, it, cpu_mask);
65826ed70ecSGuanchao Liang }
659