xref: /optee_os/core/drivers/gic.c (revision 3b3a4611ebbe6a749d76152b35236aaa5b99977e)
1b0104773SPascal Brand /*
27315b7b4SJens Wiklander  * Copyright (c) 2016, Linaro Limited
3b0104773SPascal Brand  * Copyright (c) 2014, STMicroelectronics International N.V.
4b0104773SPascal Brand  * All rights reserved.
5b0104773SPascal Brand  *
6b0104773SPascal Brand  * Redistribution and use in source and binary forms, with or without
7b0104773SPascal Brand  * modification, are permitted provided that the following conditions are met:
8b0104773SPascal Brand  *
9b0104773SPascal Brand  * 1. Redistributions of source code must retain the above copyright notice,
10b0104773SPascal Brand  * this list of conditions and the following disclaimer.
11b0104773SPascal Brand  *
12b0104773SPascal Brand  * 2. Redistributions in binary form must reproduce the above copyright notice,
13b0104773SPascal Brand  * this list of conditions and the following disclaimer in the documentation
14b0104773SPascal Brand  * and/or other materials provided with the distribution.
15b0104773SPascal Brand  *
16b0104773SPascal Brand  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17b0104773SPascal Brand  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18b0104773SPascal Brand  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19b0104773SPascal Brand  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20b0104773SPascal Brand  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21b0104773SPascal Brand  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22b0104773SPascal Brand  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23b0104773SPascal Brand  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24b0104773SPascal Brand  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25b0104773SPascal Brand  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26b0104773SPascal Brand  * POSSIBILITY OF SUCH DAMAGE.
27b0104773SPascal Brand  */
28b0104773SPascal Brand 
298ddf5a4eSEtienne Carriere #include <assert.h>
30b0104773SPascal Brand #include <drivers/gic.h>
317315b7b4SJens Wiklander #include <kernel/interrupt.h>
32d13278b8SEtienne Carriere #include <kernel/panic.h>
337315b7b4SJens Wiklander #include <util.h>
34b0104773SPascal Brand #include <io.h>
354de4bebcSJens Wiklander #include <trace.h>
36b0104773SPascal Brand 
37b0104773SPascal Brand /* Offsets from gic.gicc_base */
38b0104773SPascal Brand #define GICC_CTLR		(0x000)
3930a673e3SPeter Maydell #define GICC_PMR		(0x004)
40b0104773SPascal Brand #define GICC_IAR		(0x00C)
41b0104773SPascal Brand #define GICC_EOIR		(0x010)
42b0104773SPascal Brand 
43b0104773SPascal Brand #define GICC_CTLR_ENABLEGRP0	(1 << 0)
44b0104773SPascal Brand #define GICC_CTLR_ENABLEGRP1	(1 << 1)
45b0104773SPascal Brand #define GICC_CTLR_FIQEN		(1 << 3)
46b0104773SPascal Brand 
47b0104773SPascal Brand /* Offsets from gic.gicd_base */
48b0104773SPascal Brand #define GICD_CTLR		(0x000)
49b0104773SPascal Brand #define GICD_TYPER		(0x004)
50b0104773SPascal Brand #define GICD_IGROUPR(n)		(0x080 + (n) * 4)
51b0104773SPascal Brand #define GICD_ISENABLER(n)	(0x100 + (n) * 4)
52b0104773SPascal Brand #define GICD_ICENABLER(n)	(0x180 + (n) * 4)
5326ed70ecSGuanchao Liang #define GICD_ISPENDR(n)		(0x200 + (n) * 4)
54b0104773SPascal Brand #define GICD_ICPENDR(n)		(0x280 + (n) * 4)
55b0104773SPascal Brand #define GICD_IPRIORITYR(n)	(0x400 + (n) * 4)
56b0104773SPascal Brand #define GICD_ITARGETSR(n)	(0x800 + (n) * 4)
5726ed70ecSGuanchao Liang #define GICD_SGIR		(0xF00)
58b0104773SPascal Brand 
59b0104773SPascal Brand #define GICD_CTLR_ENABLEGRP0	(1 << 0)
60b0104773SPascal Brand #define GICD_CTLR_ENABLEGRP1	(1 << 1)
61b0104773SPascal Brand 
6253bd332aSSY Chiu /* Number of Private Peripheral Interrupt */
6353bd332aSSY Chiu #define NUM_PPI	32
6453bd332aSSY Chiu 
6526ed70ecSGuanchao Liang /* Number of Software Generated Interrupt */
6626ed70ecSGuanchao Liang #define NUM_SGI			16
6726ed70ecSGuanchao Liang 
6826ed70ecSGuanchao Liang /* Number of Non-secure Software Generated Interrupt */
6926ed70ecSGuanchao Liang #define NUM_NS_SGI		8
7026ed70ecSGuanchao Liang 
7153bd332aSSY Chiu /* Number of interrupts in one register */
7253bd332aSSY Chiu #define NUM_INTS_PER_REG	32
7353bd332aSSY Chiu 
7453bd332aSSY Chiu /* Number of targets in one register */
7553bd332aSSY Chiu #define NUM_TARGETS_PER_REG	4
7653bd332aSSY Chiu 
7753bd332aSSY Chiu /* Accessors to access ITARGETSRn */
7853bd332aSSY Chiu #define ITARGETSR_FIELD_BITS	8
7953bd332aSSY Chiu #define ITARGETSR_FIELD_MASK	0xff
8053bd332aSSY Chiu 
81b0104773SPascal Brand /* Maximum number of interrups a GIC can support */
82b0104773SPascal Brand #define GIC_MAX_INTS		1020
83b0104773SPascal Brand 
847315b7b4SJens Wiklander #define GICC_IAR_IT_ID_MASK	0x3ff
857315b7b4SJens Wiklander #define GICC_IAR_CPU_ID_MASK	0x7
867315b7b4SJens Wiklander #define GICC_IAR_CPU_ID_SHIFT	10
87b0104773SPascal Brand 
887315b7b4SJens Wiklander static void gic_op_add(struct itr_chip *chip, size_t it, uint32_t flags);
897315b7b4SJens Wiklander static void gic_op_enable(struct itr_chip *chip, size_t it);
907315b7b4SJens Wiklander static void gic_op_disable(struct itr_chip *chip, size_t it);
9126ed70ecSGuanchao Liang static void gic_op_raise_pi(struct itr_chip *chip, size_t it);
9226ed70ecSGuanchao Liang static void gic_op_raise_sgi(struct itr_chip *chip, size_t it,
9326ed70ecSGuanchao Liang 			uint8_t cpu_mask);
9426ed70ecSGuanchao Liang static void gic_op_set_affinity(struct itr_chip *chip, size_t it,
9526ed70ecSGuanchao Liang 			uint8_t cpu_mask);
967315b7b4SJens Wiklander 
977315b7b4SJens Wiklander static const struct itr_ops gic_ops = {
987315b7b4SJens Wiklander 	.add = gic_op_add,
997315b7b4SJens Wiklander 	.enable = gic_op_enable,
1007315b7b4SJens Wiklander 	.disable = gic_op_disable,
10126ed70ecSGuanchao Liang 	.raise_pi = gic_op_raise_pi,
10226ed70ecSGuanchao Liang 	.raise_sgi = gic_op_raise_sgi,
10326ed70ecSGuanchao Liang 	.set_affinity = gic_op_set_affinity,
1047315b7b4SJens Wiklander };
1057315b7b4SJens Wiklander 
1067315b7b4SJens Wiklander static size_t probe_max_it(vaddr_t gicc_base, vaddr_t gicd_base)
107b0104773SPascal Brand {
108b0104773SPascal Brand 	int i;
109b0104773SPascal Brand 	uint32_t old_ctlr;
110b0104773SPascal Brand 	size_t ret = 0;
11179f008d3SJens Wiklander 	const size_t max_regs = ((GIC_MAX_INTS + NUM_INTS_PER_REG - 1) /
11279f008d3SJens Wiklander 					NUM_INTS_PER_REG) - 1;
113b0104773SPascal Brand 
114b0104773SPascal Brand 	/*
115b0104773SPascal Brand 	 * Probe which interrupt number is the largest.
116b0104773SPascal Brand 	 */
1177315b7b4SJens Wiklander 	old_ctlr = read32(gicc_base + GICC_CTLR);
1187315b7b4SJens Wiklander 	write32(0, gicc_base + GICC_CTLR);
11979f008d3SJens Wiklander 	for (i = max_regs; i >= 0; i--) {
120b0104773SPascal Brand 		uint32_t old_reg;
121b0104773SPascal Brand 		uint32_t reg;
122b0104773SPascal Brand 		int b;
123b0104773SPascal Brand 
1247315b7b4SJens Wiklander 		old_reg = read32(gicd_base + GICD_ISENABLER(i));
1257315b7b4SJens Wiklander 		write32(0xffffffff, gicd_base + GICD_ISENABLER(i));
1267315b7b4SJens Wiklander 		reg = read32(gicd_base + GICD_ISENABLER(i));
1277315b7b4SJens Wiklander 		write32(old_reg, gicd_base + GICD_ICENABLER(i));
12879f008d3SJens Wiklander 		for (b = NUM_INTS_PER_REG - 1; b >= 0; b--) {
129007a97a2SJens Wiklander 			if (BIT32(b) & reg) {
13053bd332aSSY Chiu 				ret = i * NUM_INTS_PER_REG + b;
131b0104773SPascal Brand 				goto out;
132b0104773SPascal Brand 			}
133b0104773SPascal Brand 		}
134b0104773SPascal Brand 	}
135b0104773SPascal Brand out:
1367315b7b4SJens Wiklander 	write32(old_ctlr, gicc_base + GICC_CTLR);
137b0104773SPascal Brand 	return ret;
138b0104773SPascal Brand }
139b0104773SPascal Brand 
1407315b7b4SJens Wiklander void gic_cpu_init(struct gic_data *gd)
141bedc2b9fSsunny {
14205efe1e1SEtienne Carriere 	assert(gd->gicd_base && gd->gicc_base);
14305efe1e1SEtienne Carriere 
144e06e6e74SPeter Maydell 	/* per-CPU interrupts config:
145bedc2b9fSsunny 	 * ID0-ID7(SGI)   for Non-secure interrupts
146bedc2b9fSsunny 	 * ID8-ID15(SGI)  for Secure interrupts.
147bedc2b9fSsunny 	 * All PPI config as Non-secure interrupts.
148bedc2b9fSsunny 	 */
1497315b7b4SJens Wiklander 	write32(0xffff00ff, gd->gicd_base + GICD_IGROUPR(0));
150bedc2b9fSsunny 
15130a673e3SPeter Maydell 	/* Set the priority mask to permit Non-secure interrupts, and to
15230a673e3SPeter Maydell 	 * allow the Non-secure world to adjust the priority mask itself
15330a673e3SPeter Maydell 	 */
1547315b7b4SJens Wiklander 	write32(0x80, gd->gicc_base + GICC_PMR);
15530a673e3SPeter Maydell 
156bedc2b9fSsunny 	/* Enable GIC */
157bedc2b9fSsunny 	write32(GICC_CTLR_ENABLEGRP0 | GICC_CTLR_ENABLEGRP1 | GICC_CTLR_FIQEN,
1587315b7b4SJens Wiklander 		gd->gicc_base + GICC_CTLR);
159bedc2b9fSsunny }
160bedc2b9fSsunny 
1617315b7b4SJens Wiklander void gic_init(struct gic_data *gd, vaddr_t gicc_base, vaddr_t gicd_base)
162b0104773SPascal Brand {
163b0104773SPascal Brand 	size_t n;
164b0104773SPascal Brand 
1657315b7b4SJens Wiklander 	gic_init_base_addr(gd, gicc_base, gicd_base);
166b0104773SPascal Brand 
1677315b7b4SJens Wiklander 	for (n = 0; n <= gd->max_it / NUM_INTS_PER_REG; n++) {
168b0104773SPascal Brand 		/* Disable interrupts */
1697315b7b4SJens Wiklander 		write32(0xffffffff, gd->gicd_base + GICD_ICENABLER(n));
170b0104773SPascal Brand 
171b0104773SPascal Brand 		/* Make interrupts non-pending */
1727315b7b4SJens Wiklander 		write32(0xffffffff, gd->gicd_base + GICD_ICPENDR(n));
173b0104773SPascal Brand 
174b0104773SPascal Brand 		/* Mark interrupts non-secure */
175bedc2b9fSsunny 		if (n == 0) {
176bedc2b9fSsunny 			/* per-CPU inerrupts config:
177bedc2b9fSsunny                          * ID0-ID7(SGI)   for Non-secure interrupts
178bedc2b9fSsunny                          * ID8-ID15(SGI)  for Secure interrupts.
179bedc2b9fSsunny                          * All PPI config as Non-secure interrupts.
180bedc2b9fSsunny 			 */
1817315b7b4SJens Wiklander 			write32(0xffff00ff, gd->gicd_base + GICD_IGROUPR(n));
182bedc2b9fSsunny 		} else {
1837315b7b4SJens Wiklander 			write32(0xffffffff, gd->gicd_base + GICD_IGROUPR(n));
184b0104773SPascal Brand 		}
185bedc2b9fSsunny 	}
186b0104773SPascal Brand 
18730a673e3SPeter Maydell 	/* Set the priority mask to permit Non-secure interrupts, and to
18830a673e3SPeter Maydell 	 * allow the Non-secure world to adjust the priority mask itself
18930a673e3SPeter Maydell 	 */
1907315b7b4SJens Wiklander 	write32(0x80, gd->gicc_base + GICC_PMR);
19130a673e3SPeter Maydell 
192b0104773SPascal Brand 	/* Enable GIC */
193b0104773SPascal Brand 	write32(GICC_CTLR_ENABLEGRP0 | GICC_CTLR_ENABLEGRP1 | GICC_CTLR_FIQEN,
1947315b7b4SJens Wiklander 		gd->gicc_base + GICC_CTLR);
195b0104773SPascal Brand 	write32(GICD_CTLR_ENABLEGRP0 | GICD_CTLR_ENABLEGRP1,
1967315b7b4SJens Wiklander 		gd->gicd_base + GICD_CTLR);
197b0104773SPascal Brand }
198b0104773SPascal Brand 
1997315b7b4SJens Wiklander void gic_init_base_addr(struct gic_data *gd, vaddr_t gicc_base,
2007315b7b4SJens Wiklander 			vaddr_t gicd_base)
20153bd332aSSY Chiu {
2027315b7b4SJens Wiklander 	gd->gicc_base = gicc_base;
2037315b7b4SJens Wiklander 	gd->gicd_base = gicd_base;
2047315b7b4SJens Wiklander 	gd->max_it = probe_max_it(gicc_base, gicd_base);
2057315b7b4SJens Wiklander 	gd->chip.ops = &gic_ops;
20653bd332aSSY Chiu }
20753bd332aSSY Chiu 
2087315b7b4SJens Wiklander static void gic_it_add(struct gic_data *gd, size_t it)
209b0104773SPascal Brand {
21053bd332aSSY Chiu 	size_t idx = it / NUM_INTS_PER_REG;
21153bd332aSSY Chiu 	uint32_t mask = 1 << (it % NUM_INTS_PER_REG);
212b0104773SPascal Brand 
213b0104773SPascal Brand 	/* Disable the interrupt */
2147315b7b4SJens Wiklander 	write32(mask, gd->gicd_base + GICD_ICENABLER(idx));
215b0104773SPascal Brand 	/* Make it non-pending */
2167315b7b4SJens Wiklander 	write32(mask, gd->gicd_base + GICD_ICPENDR(idx));
217b0104773SPascal Brand 	/* Assign it to group0 */
2187315b7b4SJens Wiklander 	write32(read32(gd->gicd_base + GICD_IGROUPR(idx)) & ~mask,
2197315b7b4SJens Wiklander 			gd->gicd_base + GICD_IGROUPR(idx));
220b0104773SPascal Brand }
221b0104773SPascal Brand 
2227315b7b4SJens Wiklander static void gic_it_set_cpu_mask(struct gic_data *gd, size_t it,
2237315b7b4SJens Wiklander 				uint8_t cpu_mask)
224b0104773SPascal Brand {
2258ddf5a4eSEtienne Carriere 	size_t idx __maybe_unused = it / NUM_INTS_PER_REG;
2268ddf5a4eSEtienne Carriere 	uint32_t mask __maybe_unused = 1 << (it % NUM_INTS_PER_REG);
22753bd332aSSY Chiu 	uint32_t target, target_shift;
228b0104773SPascal Brand 
229b0104773SPascal Brand 	/* Assigned to group0 */
2307315b7b4SJens Wiklander 	assert(!(read32(gd->gicd_base + GICD_IGROUPR(idx)) & mask));
231b0104773SPascal Brand 
232b0104773SPascal Brand 	/* Route it to selected CPUs */
2337315b7b4SJens Wiklander 	target = read32(gd->gicd_base +
2347315b7b4SJens Wiklander 			GICD_ITARGETSR(it / NUM_TARGETS_PER_REG));
23553bd332aSSY Chiu 	target_shift = (it % NUM_TARGETS_PER_REG) * ITARGETSR_FIELD_BITS;
23653bd332aSSY Chiu 	target &= ~(ITARGETSR_FIELD_MASK << target_shift);
23753bd332aSSY Chiu 	target |= cpu_mask << target_shift;
2381f60363aSJens Wiklander 	DMSG("cpu_mask: writing 0x%x to 0x%" PRIxVA,
2397315b7b4SJens Wiklander 	     target, gd->gicd_base + GICD_ITARGETSR(it / NUM_TARGETS_PER_REG));
2407315b7b4SJens Wiklander 	write32(target,
2417315b7b4SJens Wiklander 		gd->gicd_base + GICD_ITARGETSR(it / NUM_TARGETS_PER_REG));
242b0104773SPascal Brand 	DMSG("cpu_mask: 0x%x\n",
2437315b7b4SJens Wiklander 	     read32(gd->gicd_base + GICD_ITARGETSR(it / NUM_TARGETS_PER_REG)));
244b0104773SPascal Brand }
245b0104773SPascal Brand 
2467315b7b4SJens Wiklander static void gic_it_set_prio(struct gic_data *gd, size_t it, uint8_t prio)
247b0104773SPascal Brand {
2488ddf5a4eSEtienne Carriere 	size_t idx __maybe_unused = it / NUM_INTS_PER_REG;
2498ddf5a4eSEtienne Carriere 	uint32_t mask __maybe_unused = 1 << (it % NUM_INTS_PER_REG);
250b0104773SPascal Brand 
251b0104773SPascal Brand 	/* Assigned to group0 */
2527315b7b4SJens Wiklander 	assert(!(read32(gd->gicd_base + GICD_IGROUPR(idx)) & mask));
253b0104773SPascal Brand 
254b0104773SPascal Brand 	/* Set prio it to selected CPUs */
2551f60363aSJens Wiklander 	DMSG("prio: writing 0x%x to 0x%" PRIxVA,
2567315b7b4SJens Wiklander 		prio, gd->gicd_base + GICD_IPRIORITYR(0) + it);
2577315b7b4SJens Wiklander 	write8(prio, gd->gicd_base + GICD_IPRIORITYR(0) + it);
258b0104773SPascal Brand }
259b0104773SPascal Brand 
2607315b7b4SJens Wiklander static void gic_it_enable(struct gic_data *gd, size_t it)
261b0104773SPascal Brand {
26253bd332aSSY Chiu 	size_t idx = it / NUM_INTS_PER_REG;
26353bd332aSSY Chiu 	uint32_t mask = 1 << (it % NUM_INTS_PER_REG);
264b0104773SPascal Brand 
265b0104773SPascal Brand 	/* Assigned to group0 */
2667315b7b4SJens Wiklander 	assert(!(read32(gd->gicd_base + GICD_IGROUPR(idx)) & mask));
26726ed70ecSGuanchao Liang 	if (it >= NUM_SGI) {
26826ed70ecSGuanchao Liang 		/*
26926ed70ecSGuanchao Liang 		 * Not enabled yet, except Software Generated Interrupt
27026ed70ecSGuanchao Liang 		 * which is implementation defined
27126ed70ecSGuanchao Liang 		 */
2727315b7b4SJens Wiklander 		assert(!(read32(gd->gicd_base + GICD_ISENABLER(idx)) & mask));
27326ed70ecSGuanchao Liang 	}
274b0104773SPascal Brand 
275b0104773SPascal Brand 	/* Enable the interrupt */
2767315b7b4SJens Wiklander 	write32(mask, gd->gicd_base + GICD_ISENABLER(idx));
277b0104773SPascal Brand }
278b0104773SPascal Brand 
2797315b7b4SJens Wiklander static void gic_it_disable(struct gic_data *gd, size_t it)
280b0104773SPascal Brand {
28153bd332aSSY Chiu 	size_t idx = it / NUM_INTS_PER_REG;
28253bd332aSSY Chiu 	uint32_t mask = 1 << (it % NUM_INTS_PER_REG);
283b0104773SPascal Brand 
284b0104773SPascal Brand 	/* Assigned to group0 */
2857315b7b4SJens Wiklander 	assert(!(read32(gd->gicd_base + GICD_IGROUPR(idx)) & mask));
286b0104773SPascal Brand 
287b0104773SPascal Brand 	/* Disable the interrupt */
2887315b7b4SJens Wiklander 	write32(mask, gd->gicd_base + GICD_ICENABLER(idx));
289b0104773SPascal Brand }
290b0104773SPascal Brand 
29126ed70ecSGuanchao Liang static void gic_it_set_pending(struct gic_data *gd, size_t it)
29226ed70ecSGuanchao Liang {
29326ed70ecSGuanchao Liang 	size_t idx = it / NUM_INTS_PER_REG;
29426ed70ecSGuanchao Liang 	uint32_t mask = BIT32(it % NUM_INTS_PER_REG);
29526ed70ecSGuanchao Liang 
29626ed70ecSGuanchao Liang 	/* Should be Peripheral Interrupt */
29726ed70ecSGuanchao Liang 	assert(it >= NUM_SGI);
29826ed70ecSGuanchao Liang 	/* Assigned to group0 */
29926ed70ecSGuanchao Liang 	assert(!(read32(gd->gicd_base + GICD_IGROUPR(idx)) & mask));
30026ed70ecSGuanchao Liang 
30126ed70ecSGuanchao Liang 	/* Raise the interrupt */
30226ed70ecSGuanchao Liang 	write32(mask, gd->gicd_base + GICD_ISPENDR(idx));
30326ed70ecSGuanchao Liang }
30426ed70ecSGuanchao Liang 
30526ed70ecSGuanchao Liang static void gic_it_raise_sgi(struct gic_data *gd, size_t it,
30626ed70ecSGuanchao Liang 		uint8_t cpu_mask, uint8_t group)
30726ed70ecSGuanchao Liang {
30826ed70ecSGuanchao Liang 	uint32_t mask_id = it & 0xf;
30926ed70ecSGuanchao Liang 	uint32_t mask_group = group & 0x1;
31026ed70ecSGuanchao Liang 	uint32_t mask_cpu = cpu_mask & 0xff;
31126ed70ecSGuanchao Liang 	uint32_t mask = (mask_id | SHIFT_U32(mask_group, 15) |
31226ed70ecSGuanchao Liang 		SHIFT_U32(mask_cpu, 16));
31326ed70ecSGuanchao Liang 
31426ed70ecSGuanchao Liang 	/* Should be Software Generated Interrupt */
31526ed70ecSGuanchao Liang 	assert(it < NUM_SGI);
31626ed70ecSGuanchao Liang 
31726ed70ecSGuanchao Liang 	/* Raise the interrupt */
31826ed70ecSGuanchao Liang 	write32(mask, gd->gicd_base + GICD_SGIR);
31926ed70ecSGuanchao Liang }
32026ed70ecSGuanchao Liang 
3217315b7b4SJens Wiklander static uint32_t gic_read_iar(struct gic_data *gd)
322b0104773SPascal Brand {
3237315b7b4SJens Wiklander 	return read32(gd->gicc_base + GICC_IAR);
324b0104773SPascal Brand }
325b0104773SPascal Brand 
3267315b7b4SJens Wiklander static void gic_write_eoir(struct gic_data *gd, uint32_t eoir)
327b0104773SPascal Brand {
3287315b7b4SJens Wiklander 	write32(eoir, gd->gicc_base + GICC_EOIR);
329b0104773SPascal Brand }
330b0104773SPascal Brand 
3317315b7b4SJens Wiklander static bool gic_it_is_enabled(struct gic_data *gd, size_t it)
3327315b7b4SJens Wiklander {
33353bd332aSSY Chiu 	size_t idx = it / NUM_INTS_PER_REG;
33453bd332aSSY Chiu 	uint32_t mask = 1 << (it % NUM_INTS_PER_REG);
3357315b7b4SJens Wiklander 	return !!(read32(gd->gicd_base + GICD_ISENABLER(idx)) & mask);
33653bd332aSSY Chiu }
33753bd332aSSY Chiu 
3387315b7b4SJens Wiklander static bool __maybe_unused gic_it_get_group(struct gic_data *gd, size_t it)
3397315b7b4SJens Wiklander {
34053bd332aSSY Chiu 	size_t idx = it / NUM_INTS_PER_REG;
34153bd332aSSY Chiu 	uint32_t mask = 1 << (it % NUM_INTS_PER_REG);
3427315b7b4SJens Wiklander 	return !!(read32(gd->gicd_base + GICD_IGROUPR(idx)) & mask);
34353bd332aSSY Chiu }
34453bd332aSSY Chiu 
3457315b7b4SJens Wiklander static uint32_t __maybe_unused gic_it_get_target(struct gic_data *gd, size_t it)
3467315b7b4SJens Wiklander {
34753bd332aSSY Chiu 	size_t reg_idx = it / NUM_TARGETS_PER_REG;
3487315b7b4SJens Wiklander 	uint32_t target_shift = (it % NUM_TARGETS_PER_REG) *
3497315b7b4SJens Wiklander 				ITARGETSR_FIELD_BITS;
35053bd332aSSY Chiu 	uint32_t target_mask = ITARGETSR_FIELD_MASK << target_shift;
35153bd332aSSY Chiu 	uint32_t target =
3527315b7b4SJens Wiklander 		read32(gd->gicd_base + GICD_ITARGETSR(reg_idx)) & target_mask;
3537315b7b4SJens Wiklander 
35453bd332aSSY Chiu 	target = target >> target_shift;
35553bd332aSSY Chiu 	return target;
35653bd332aSSY Chiu }
35753bd332aSSY Chiu 
3587315b7b4SJens Wiklander void gic_dump_state(struct gic_data *gd)
35953bd332aSSY Chiu {
36053bd332aSSY Chiu 	int i;
36153bd332aSSY Chiu 
3627315b7b4SJens Wiklander 	DMSG("GICC_CTLR: 0x%x", read32(gd->gicc_base + GICC_CTLR));
3637315b7b4SJens Wiklander 	DMSG("GICD_CTLR: 0x%x", read32(gd->gicd_base + GICD_CTLR));
3647315b7b4SJens Wiklander 
3657315b7b4SJens Wiklander 	for (i = 0; i < (int)gd->max_it; i++) {
3667315b7b4SJens Wiklander 		if (gic_it_is_enabled(gd, i)) {
36753bd332aSSY Chiu 			DMSG("irq%d: enabled, group:%d, target:%x", i,
3687315b7b4SJens Wiklander 			     gic_it_get_group(gd, i), gic_it_get_target(gd, i));
36953bd332aSSY Chiu 		}
37053bd332aSSY Chiu 	}
37153bd332aSSY Chiu }
3727315b7b4SJens Wiklander 
3737315b7b4SJens Wiklander void gic_it_handle(struct gic_data *gd)
3747315b7b4SJens Wiklander {
3757315b7b4SJens Wiklander 	uint32_t iar;
3767315b7b4SJens Wiklander 	uint32_t id;
3777315b7b4SJens Wiklander 
3787315b7b4SJens Wiklander 	iar = gic_read_iar(gd);
3797315b7b4SJens Wiklander 	id = iar & GICC_IAR_IT_ID_MASK;
3807315b7b4SJens Wiklander 
381*3b3a4611SMathieu Briand 	if (id < gd->max_it)
3827315b7b4SJens Wiklander 		itr_handle(id);
383*3b3a4611SMathieu Briand 	else
384*3b3a4611SMathieu Briand 		DMSG("ignoring interrupt %" PRIu32, id);
3857315b7b4SJens Wiklander 
3867315b7b4SJens Wiklander 	gic_write_eoir(gd, iar);
3877315b7b4SJens Wiklander }
3887315b7b4SJens Wiklander 
3897315b7b4SJens Wiklander static void gic_op_add(struct itr_chip *chip, size_t it,
3907315b7b4SJens Wiklander 		       uint32_t flags __unused)
3917315b7b4SJens Wiklander {
3927315b7b4SJens Wiklander 	struct gic_data *gd = container_of(chip, struct gic_data, chip);
3937315b7b4SJens Wiklander 
394d13278b8SEtienne Carriere 	if (it >= gd->max_it)
395d13278b8SEtienne Carriere 		panic();
396d13278b8SEtienne Carriere 
3977315b7b4SJens Wiklander 	gic_it_add(gd, it);
3987315b7b4SJens Wiklander 	/* Set the CPU mask to deliver interrupts to any online core */
3997315b7b4SJens Wiklander 	gic_it_set_cpu_mask(gd, it, 0xff);
4007315b7b4SJens Wiklander 	gic_it_set_prio(gd, it, 0x1);
4017315b7b4SJens Wiklander }
4027315b7b4SJens Wiklander 
4037315b7b4SJens Wiklander static void gic_op_enable(struct itr_chip *chip, size_t it)
4047315b7b4SJens Wiklander {
4057315b7b4SJens Wiklander 	struct gic_data *gd = container_of(chip, struct gic_data, chip);
4067315b7b4SJens Wiklander 
407d13278b8SEtienne Carriere 	if (it >= gd->max_it)
408d13278b8SEtienne Carriere 		panic();
409d13278b8SEtienne Carriere 
4107315b7b4SJens Wiklander 	gic_it_enable(gd, it);
4117315b7b4SJens Wiklander }
4127315b7b4SJens Wiklander 
4137315b7b4SJens Wiklander static void gic_op_disable(struct itr_chip *chip, size_t it)
4147315b7b4SJens Wiklander {
4157315b7b4SJens Wiklander 	struct gic_data *gd = container_of(chip, struct gic_data, chip);
4167315b7b4SJens Wiklander 
417d13278b8SEtienne Carriere 	if (it >= gd->max_it)
418d13278b8SEtienne Carriere 		panic();
419d13278b8SEtienne Carriere 
4207315b7b4SJens Wiklander 	gic_it_disable(gd, it);
4217315b7b4SJens Wiklander }
42226ed70ecSGuanchao Liang 
42326ed70ecSGuanchao Liang static void gic_op_raise_pi(struct itr_chip *chip, size_t it)
42426ed70ecSGuanchao Liang {
42526ed70ecSGuanchao Liang 	struct gic_data *gd = container_of(chip, struct gic_data, chip);
42626ed70ecSGuanchao Liang 
42726ed70ecSGuanchao Liang 	if (it >= gd->max_it)
42826ed70ecSGuanchao Liang 		panic();
42926ed70ecSGuanchao Liang 
43026ed70ecSGuanchao Liang 	gic_it_set_pending(gd, it);
43126ed70ecSGuanchao Liang }
43226ed70ecSGuanchao Liang 
43326ed70ecSGuanchao Liang static void gic_op_raise_sgi(struct itr_chip *chip, size_t it,
43426ed70ecSGuanchao Liang 			uint8_t cpu_mask)
43526ed70ecSGuanchao Liang {
43626ed70ecSGuanchao Liang 	struct gic_data *gd = container_of(chip, struct gic_data, chip);
43726ed70ecSGuanchao Liang 
43826ed70ecSGuanchao Liang 	if (it >= gd->max_it)
43926ed70ecSGuanchao Liang 		panic();
44026ed70ecSGuanchao Liang 
44126ed70ecSGuanchao Liang 	if (it < NUM_NS_SGI)
44226ed70ecSGuanchao Liang 		gic_it_raise_sgi(gd, it, cpu_mask, 1);
44326ed70ecSGuanchao Liang 	else
44426ed70ecSGuanchao Liang 		gic_it_raise_sgi(gd, it, cpu_mask, 0);
44526ed70ecSGuanchao Liang }
44626ed70ecSGuanchao Liang static void gic_op_set_affinity(struct itr_chip *chip, size_t it,
44726ed70ecSGuanchao Liang 			uint8_t cpu_mask)
44826ed70ecSGuanchao Liang {
44926ed70ecSGuanchao Liang 	struct gic_data *gd = container_of(chip, struct gic_data, chip);
45026ed70ecSGuanchao Liang 
45126ed70ecSGuanchao Liang 	if (it >= gd->max_it)
45226ed70ecSGuanchao Liang 		panic();
45326ed70ecSGuanchao Liang 
45426ed70ecSGuanchao Liang 	gic_it_set_cpu_mask(gd, it, cpu_mask);
45526ed70ecSGuanchao Liang }
456