xref: /rk3399_rockchip-uboot/drivers/irq/irq-gic.c (revision ed837edf989da43204ef4e9b5d8d34b82d38dd8c)
1 /*
2  * (C) Copyright 2017 Rockchip Electronics Co., Ltd
3  *
4  * SPDX-License-Identifier:     GPL-2.0+
5  */
6 
7 #include <asm/io.h>
8 #include <asm/gic.h>
9 #include <config.h>
10 #include <irq-generic.h>
11 #include "irq-gic.h"
12 
13 #define gicd_readl(offset)	readl(GICD_BASE + (offset))
14 #define gicc_readl(offset)	readl(GICC_BASE + (offset))
15 #define gicd_writel(v, offset)	writel(v, GICD_BASE + (offset))
16 #define gicc_writel(v, offset)	writel(v, GICC_BASE + (offset))
17 
18 typedef enum INT_TRIG {
19 	INT_LEVEL_TRIGGER,
20 	INT_EDGE_TRIGGER
21 } eINT_TRIG;
22 
23 typedef enum INT_SECURE {
24 	INT_SECURE,
25 	INT_NOSECURE
26 } eINT_SECURE;
27 
28 typedef enum INT_SIGTYPE {
29 	INT_SIGTYPE_IRQ,
30 	INT_SIGTYPE_FIQ
31 } eINT_SIGTYPE;
32 
33 #define g_gicd		((pGICD_REG)GICD_BASE)
34 #define g_gicc		((pGICC_REG)GICC_BASE)
35 
36 __maybe_unused static u8 g_gic_cpumask = 0x01;
37 
38 static inline void int_set_prio_filter(u32 nprio)
39 {
40 	g_gicc->iccpmr = (nprio & 0xff);
41 }
42 
43 static inline void int_enable_distributor(void)
44 {
45 	g_gicd->icddcr = 0x01;
46 }
47 
48 static inline void int_disable_distributor(void)
49 {
50 	g_gicd->icddcr = 0x00;
51 }
52 
53 static inline void int_enable_secure_signal(void)
54 {
55 	g_gicc->iccicr |= 0x01;
56 }
57 
58 static inline void int_disable_secure_signal(void)
59 {
60 	g_gicc->iccicr &= (~0x01);
61 }
62 
63 static inline void int_enable_nosecure_signal(void)
64 {
65 	g_gicc->iccicr |= 0x02;
66 }
67 
68 static inline void int_disable_nosecure_signal(void)
69 {
70 	g_gicc->iccicr &= (~0x02);
71 }
72 
73 static int gic_irq_set_trigger(int irq, eINT_TRIG ntrig)
74 {
75 	u32 group, offset;
76 
77 	if (irq >= PLATFORM_GIC_IRQS_NR)
78 		return -EINVAL;
79 
80 	group = irq / 16;
81 	offset = irq % 16;
82 
83 	if (ntrig == INT_LEVEL_TRIGGER)
84 		g_gicd->icdicfr[group] &= (~(1 << (2 * offset + 1)));
85 	else
86 		g_gicd->icdicfr[group] |= (1 << (2 * offset + 1));
87 
88 	return 0;
89 }
90 
91 __maybe_unused static int gic_irq_set_pending(int irq)
92 {
93 	u32 group, offset;
94 
95 	if (irq >= PLATFORM_GIC_IRQS_NR)
96 		return -EINVAL;
97 
98 	group = irq / 32;
99 	offset = irq % 32;
100 	g_gicd->icdispr[group] = (0x1 << offset);
101 
102 	return 0;
103 }
104 
105 __maybe_unused static int gic_irq_clear_pending(int irq)
106 {
107 	u32 group, offset;
108 
109 	if (irq >= PLATFORM_GIC_IRQS_NR)
110 		return -EINVAL;
111 
112 	group = irq / 32;
113 	offset = irq % 32;
114 	g_gicd->icdicpr[group] = (0x1 << offset);
115 
116 	return 0;
117 }
118 
119 __maybe_unused static int gic_irq_set_secure(int irq, eINT_SECURE nsecure)
120 {
121 	u32 group, offset;
122 
123 	if (irq >= PLATFORM_GIC_IRQS_NR)
124 		return -EINVAL;
125 
126 	group = irq / 32;
127 	offset = irq % 32;
128 	g_gicd->icdiser[group] |= nsecure << offset;
129 
130 	return 0;
131 }
132 
133 __maybe_unused static u32 gic_get_cpumask(void)
134 {
135 	u32 mask = 0, i;
136 
137 	for (i = mask = 0; i < 32; i += 4) {
138 		mask = g_gicd->itargetsr[i];
139 		mask |= mask >> 16;
140 		mask |= mask >> 8;
141 		if (mask)
142 			break;
143 	}
144 
145 	if (!mask)
146 		printf("GIC CPU mask not found.\n");
147 
148 	debug("GIC CPU mask = 0x%08x\n", mask);
149 
150 	return mask;
151 }
152 
153 static int gic_irq_enable(int irq)
154 {
155 #ifdef CONFIG_GICV2
156 	u32 shift = (irq % 4) * 8;
157 	u32 offset = (irq / 4);
158 	u32 M, N;
159 
160 	if (irq >= PLATFORM_GIC_IRQS_NR)
161 		return -EINVAL;
162 
163 	M = irq / 32;
164 	N = irq % 32;
165 
166 	g_gicc->iccicr &= (~0x08);
167 	g_gicd->icdiser[M] = (0x1 << N);
168 	g_gicd->itargetsr[offset] &= ~(0xFF << shift);
169 	g_gicd->itargetsr[offset] |= (g_gic_cpumask << shift);
170 #else
171 	u32 M, N;
172 
173 	if (irq >= PLATFORM_GIC_IRQS_NR)
174 		return -EINVAL;
175 
176 	M = irq / 32;
177 	N = irq % 32;
178 	g_gicd->icdiser[M] = (0x1 << N);
179 #endif
180 
181 	return 0;
182 }
183 
184 static int gic_irq_disable(int irq)
185 {
186 	u32 group, offset;
187 
188 	if (irq >= PLATFORM_GIC_IRQS_NR)
189 		return -EINVAL;
190 
191 	group = irq / 32;
192 	offset = irq % 32;
193 	g_gicd->icdicer[group] = (0x1 << offset);
194 
195 	return 0;
196 }
197 
198 /*
199  * irq_set_type - set the irq trigger type for an irq
200  *
201  * @irq: irq number
202  * @type: IRQ_TYPE_{LEVEL,EDGE}_* value - see asm/arch/irq.h
203  */
204 static int gic_irq_set_type(int irq, unsigned int type)
205 {
206 	unsigned int int_type;
207 
208 	switch (type) {
209 	case IRQ_TYPE_EDGE_RISING:
210 	case IRQ_TYPE_EDGE_FALLING:
211 		int_type = INT_EDGE_TRIGGER;
212 		break;
213 	case IRQ_TYPE_LEVEL_HIGH:
214 	case IRQ_TYPE_LEVEL_LOW:
215 		int_type = INT_LEVEL_TRIGGER;
216 		break;
217 	default:
218 		return -EINVAL;
219 	}
220 
221 	gic_irq_set_trigger(irq, int_type);
222 
223 	return 0;
224 }
225 
226 static void gic_irq_eoi(int irq)
227 {
228 #ifdef CONFIG_GICV2
229 	g_gicc->icceoir = irq;
230 #else
231 	asm volatile("msr " __stringify(ICC_EOIR1_EL1) ", %0"
232 			: : "r" ((u64)irq));
233 	asm volatile("msr " __stringify(ICC_DIR_EL1) ", %0"
234 			: : "r" ((u64)irq));
235 	isb();
236 #endif
237 }
238 
239 static int gic_irq_get(void)
240 {
241 #ifdef CONFIG_GICV2
242 	return g_gicc->icciar & 0x3ff; /* bit9 - bit0 */
243 #else
244 	u64 irqstat;
245 
246 	asm volatile("mrs %0, " __stringify(ICC_IAR1_EL1) : "=r" (irqstat));
247 	return (u32)irqstat & 0x3ff;
248 #endif
249 }
250 
251 struct gic_dist_data {
252 	uint32_t ctlr;
253 	uint32_t icfgr[DIV_ROUND_UP(1020, 16)];
254 	uint32_t itargetsr[DIV_ROUND_UP(1020, 4)];
255 	uint32_t ipriorityr[DIV_ROUND_UP(1020, 4)];
256 	uint32_t igroupr[DIV_ROUND_UP(1020, 32)];
257 	uint32_t ispendr[DIV_ROUND_UP(1020, 32)];
258 	uint32_t isenabler[DIV_ROUND_UP(1020, 32)];
259 };
260 
261 struct gic_cpu_data {
262 	uint32_t ctlr;
263 	uint32_t pmr;
264 };
265 
266 static struct gic_dist_data gicd_save;
267 static struct gic_cpu_data gicc_save;
268 
269 #define IRQ_REG_X4(irq)		(4 * ((irq) / 4))
270 #define IRQ_REG_X16(irq)	(4 * ((irq) / 16))
271 #define IRQ_REG_X32(irq)	(4 * ((irq) / 32))
272 
273 static int gic_irq_suspend(void)
274 {
275 	int irq_nr, i, irq;
276 
277 	/* irq nr */
278 	irq_nr = ((gicd_readl(GICD_TYPER) & 0x1f) + 1) * 32;
279 	if (irq_nr > 1020)
280 		irq_nr = 1020;
281 
282 	/* GICC save */
283 	gicc_save.ctlr = gicc_readl(GICC_CTLR);
284 	gicc_save.pmr = gicc_readl(GICC_PMR);
285 
286 	/* GICD save */
287 	gicd_save.ctlr = gicd_readl(GICD_CTLR);
288 
289 	for (i = 0, irq = 0; irq < irq_nr; irq += 16)
290 		gicd_save.icfgr[i++] = gicd_readl(GICD_ICFGR + IRQ_REG_X16(irq));
291 
292 	for (i = 0, irq = 0; irq < irq_nr; irq += 4)
293 		gicd_save.itargetsr[i++] = gicd_readl(GICD_ITARGETSRn + IRQ_REG_X4(irq));
294 
295 	for (i = 0, irq = 0; irq < irq_nr; irq += 4)
296 		gicd_save.ipriorityr[i++] = gicd_readl(GICD_IPRIORITYRn + IRQ_REG_X4(irq));
297 
298 	for (i = 0, irq = 0; irq < irq_nr; irq += 32)
299 		gicd_save.igroupr[i++] = gicd_readl(GICD_IGROUPRn + IRQ_REG_X32(irq));
300 
301 	for (i = 0, irq = 0; irq < irq_nr; irq += 32)
302 		gicd_save.ispendr[i++] = gicd_readl(GICD_ISPENDRn + IRQ_REG_X32(irq));
303 
304 	for (i = 0, irq = 0; irq < irq_nr; irq += 32)
305 		gicd_save.isenabler[i++] = gicd_readl(GICD_ISENABLERn + IRQ_REG_X32(irq));
306 
307 	dsb();
308 
309 	return 0;
310 }
311 
312 static int gic_irq_resume(void)
313 {
314 	int irq_nr, i, irq;
315 
316 	irq_nr = ((gicd_readl(GICD_TYPER) & 0x1f) + 1) * 32;
317 	if (irq_nr > 1020)
318 		irq_nr = 1020;
319 
320 	/* Disable ctrl register */
321 	gicc_writel(0, GICC_CTLR);
322 	gicd_writel(0, GICD_CTLR);
323 	dsb();
324 
325 	/* Clear all interrupt */
326 	for (i = 0, irq = 0; irq < irq_nr; irq += 32)
327 		gicd_writel(0xffffffff, GICD_ICENABLERn + IRQ_REG_X32(irq));
328 
329 	for (i = 0, irq = 0; irq < irq_nr; irq += 16)
330 		gicd_writel(gicd_save.icfgr[i++], GICD_ICFGR + IRQ_REG_X16(irq));
331 
332 	for (i = 0, irq = 0; irq < irq_nr; irq += 4)
333 		gicd_writel(gicd_save.itargetsr[i++], GICD_ITARGETSRn + IRQ_REG_X4(irq));
334 
335 	for (i = 0, irq = 0; irq < irq_nr; irq += 4)
336 		gicd_writel(gicd_save.ipriorityr[i++], GICD_IPRIORITYRn + IRQ_REG_X4(irq));
337 
338 	for (i = 0, irq = 0; irq < irq_nr; irq += 32)
339 		gicd_writel(gicd_save.igroupr[i++], GICD_IGROUPRn + IRQ_REG_X32(irq));
340 
341 	for (i = 0, irq = 0; irq < irq_nr; irq += 32)
342 		gicd_writel(gicd_save.isenabler[i++], GICD_ISENABLERn + IRQ_REG_X32(irq));
343 
344 	for (i = 0, irq = 0; irq < irq_nr; irq += 32)
345 		gicd_writel(gicd_save.ispendr[i++], GICD_ISPENDRn + IRQ_REG_X32(irq));
346 	dsb();
347 
348 	gicc_writel(gicc_save.pmr, GICC_PMR);
349 	gicc_writel(gicc_save.ctlr, GICC_CTLR);
350 	gicd_writel(gicd_save.ctlr, GICD_CTLR);
351 	dsb();
352 
353 	return 0;
354 }
355 
356 /**************************************regs save and resume**************************/
357 static int gic_irq_init(void)
358 {
359 	/* GICV3 done in: arch/arm/cpu/armv8/start.S */
360 #ifdef CONFIG_GICV2
361 	/* end of interrupt */
362 	g_gicc->icceoir = PLATFORM_GIC_IRQS_NR;
363 
364 	/* disable gicc and gicd */
365 	g_gicc->iccicr = 0x00;
366 	g_gicd->icddcr = 0x00;
367 
368 	/* enable interrupt */
369 	g_gicd->icdicer[0] = 0xFFFFFFFF;
370 	g_gicd->icdicer[1] = 0xFFFFFFFF;
371 	g_gicd->icdicer[2] = 0xFFFFFFFF;
372 	g_gicd->icdicer[3] = 0xFFFFFFFF;
373 	g_gicd->icdicfr[3] &= ~(1 << 1);
374 
375 	/* set interrupt priority threhold min: 256 */
376 	int_set_prio_filter(0xff);
377 	int_enable_secure_signal();
378 	int_enable_nosecure_signal();
379 	int_enable_distributor();
380 
381 	g_gic_cpumask = gic_get_cpumask();
382 #endif
383 
384 	return 0;
385 }
386 
387 static struct irq_chip gic_irq_chip = {
388 	.name		= "gic-irq-chip",
389 	.irq_init	= gic_irq_init,
390 	.irq_suspend	= gic_irq_suspend,
391 	.irq_resume	= gic_irq_resume,
392 	.irq_get	= gic_irq_get,
393 	.irq_enable	= gic_irq_enable,
394 	.irq_disable	= gic_irq_disable,
395 	.irq_eoi	= gic_irq_eoi,
396 	.irq_set_type	= gic_irq_set_type,
397 };
398 
399 struct irq_chip *arch_gic_irq_init(void)
400 {
401 	return &gic_irq_chip;
402 }
403