xref: /rk3399_rockchip-uboot/drivers/irq/irq-generic.c (revision ed837edf989da43204ef4e9b5d8d34b82d38dd8c)
14e6670feSJoseph Chen /*
24e6670feSJoseph Chen  * (C) Copyright 2017 Rockchip Electronics Co., Ltd
34e6670feSJoseph Chen  *
44e6670feSJoseph Chen  * SPDX-License-Identifier:     GPL-2.0+
54e6670feSJoseph Chen  */
64e6670feSJoseph Chen 
74e6670feSJoseph Chen #include <asm/io.h>
84e6670feSJoseph Chen #include <asm/u-boot-arm.h>
94e6670feSJoseph Chen #include <irq-generic.h>
104e6670feSJoseph Chen #include "irq-gic.h"
114e6670feSJoseph Chen #include "irq-gpio.h"
124e6670feSJoseph Chen 
134e6670feSJoseph Chen DECLARE_GLOBAL_DATA_PTR;
144e6670feSJoseph Chen 
154e6670feSJoseph Chen struct irq_desc {
1642865eb5SJoseph Chen 	interrupt_handler_t *handle_irq;
1742865eb5SJoseph Chen 	void *data;
184e6670feSJoseph Chen };
194e6670feSJoseph Chen 
204e6670feSJoseph Chen static struct irq_desc irqs_desc[PLATFORM_MAX_IRQS_NR];
214e6670feSJoseph Chen static struct irq_chip *gic_irq_chip, *gpio_irq_chip;
224e6670feSJoseph Chen static bool initialized;
234e6670feSJoseph Chen 
244e6670feSJoseph Chen static int irq_bad(int irq)
254e6670feSJoseph Chen {
264e6670feSJoseph Chen 	if (irq >= PLATFORM_MAX_IRQS_NR) {
274e6670feSJoseph Chen 		printf("WARN: IRQ %d is out of max supported IRQ %d\n",
284e6670feSJoseph Chen 		       irq, PLATFORM_MAX_IRQS_NR);
294e6670feSJoseph Chen 		return -EINVAL;
304e6670feSJoseph Chen 	}
314e6670feSJoseph Chen 
324e6670feSJoseph Chen 	if (!initialized) {
334e6670feSJoseph Chen 		printf("WARN: Interrupt framework is not initialized\n");
344e6670feSJoseph Chen 		return -EINVAL;
354e6670feSJoseph Chen 	}
364e6670feSJoseph Chen 
374e6670feSJoseph Chen 	return 0;
384e6670feSJoseph Chen }
394e6670feSJoseph Chen 
404e6670feSJoseph Chen /* general interrupt handler for gpio chip */
4142865eb5SJoseph Chen void _generic_gpio_handle_irq(int irq)
424e6670feSJoseph Chen {
434e6670feSJoseph Chen 	if (irq_bad(irq))
444e6670feSJoseph Chen 		return;
454e6670feSJoseph Chen 
464e6670feSJoseph Chen 	if (irq < PLATFORM_GIC_IRQS_NR) {
474e6670feSJoseph Chen 		printf("WRAN: IRQ %d is not a GPIO irq\n", irq);
484e6670feSJoseph Chen 		return;
494e6670feSJoseph Chen 	}
504e6670feSJoseph Chen 
514e6670feSJoseph Chen 	if (irqs_desc[irq].handle_irq)
5242865eb5SJoseph Chen 		irqs_desc[irq].handle_irq(irq, irqs_desc[irq].data);
534e6670feSJoseph Chen }
544e6670feSJoseph Chen 
554e6670feSJoseph Chen void _do_generic_irq_handler(void)
564e6670feSJoseph Chen {
574e6670feSJoseph Chen 	u32 irq = gic_irq_chip->irq_get();
584e6670feSJoseph Chen 
594e6670feSJoseph Chen 	if (irq < PLATFORM_GIC_IRQS_NR) {
604e6670feSJoseph Chen 		if (irqs_desc[irq].handle_irq)
6142865eb5SJoseph Chen 			irqs_desc[irq].handle_irq(irq, irqs_desc[irq].data);
624e6670feSJoseph Chen 	}
634e6670feSJoseph Chen 
644e6670feSJoseph Chen 	gic_irq_chip->irq_eoi(irq);
654e6670feSJoseph Chen }
664e6670feSJoseph Chen 
674e6670feSJoseph Chen static int chip_irq_bad(struct irq_chip *chip)
684e6670feSJoseph Chen {
694e6670feSJoseph Chen 	if (!chip->name ||
704e6670feSJoseph Chen 	    !chip->irq_init ||
714e6670feSJoseph Chen 	    !chip->irq_enable ||
724e6670feSJoseph Chen 	    !chip->irq_disable ||
734e6670feSJoseph Chen 	    !chip->irq_set_type)
744e6670feSJoseph Chen 		return -EINVAL;
754e6670feSJoseph Chen 
764e6670feSJoseph Chen 	return 0;
774e6670feSJoseph Chen }
784e6670feSJoseph Chen 
794e6670feSJoseph Chen static int _do_arch_irq_init(void)
804e6670feSJoseph Chen {
814e6670feSJoseph Chen 	int irq, err = -EINVAL;
824e6670feSJoseph Chen 
834e6670feSJoseph Chen 	/* After relocation done, bss data initialized */
844e6670feSJoseph Chen 	if (!(gd->flags & GD_FLG_RELOC)) {
854e6670feSJoseph Chen 		printf("WARN: interrupt should be init after reloc\n");
864e6670feSJoseph Chen 		return -EINVAL;
874e6670feSJoseph Chen 	}
884e6670feSJoseph Chen 
894e6670feSJoseph Chen 	/*
904e6670feSJoseph Chen 	 * should set true before arch_gpio_irq_init(), otherwise
914e6670feSJoseph Chen 	 *  can't request irqs for gpio banks.
924e6670feSJoseph Chen 	 */
934e6670feSJoseph Chen 	initialized = true;
944e6670feSJoseph Chen 
9542865eb5SJoseph Chen 	for (irq = 0; irq < PLATFORM_MAX_IRQS_NR; irq++) {
964e6670feSJoseph Chen 		irqs_desc[irq].handle_irq = NULL;
9742865eb5SJoseph Chen 		irqs_desc[irq].data = NULL;
9842865eb5SJoseph Chen 	}
994e6670feSJoseph Chen 
1004e6670feSJoseph Chen 	gic_irq_chip = arch_gic_irq_init();
1014e6670feSJoseph Chen 	if (chip_irq_bad(gic_irq_chip)) {
1024e6670feSJoseph Chen 		printf("ERROR: bad gic irq chip\n");
1034e6670feSJoseph Chen 		goto out;
1044e6670feSJoseph Chen 	}
1054e6670feSJoseph Chen 
1064e6670feSJoseph Chen 	gpio_irq_chip = arch_gpio_irq_init();
1074e6670feSJoseph Chen 	if (chip_irq_bad(gpio_irq_chip)) {
1084e6670feSJoseph Chen 		printf("ERROR: bad gpio irq chip\n");
1094e6670feSJoseph Chen 		goto out;
1104e6670feSJoseph Chen 	}
1114e6670feSJoseph Chen 
1124e6670feSJoseph Chen 	err = gic_irq_chip->irq_init();
1134e6670feSJoseph Chen 	if (err) {
1144e6670feSJoseph Chen 		printf("ERROR: gic interrupt init failed\n");
1154e6670feSJoseph Chen 		goto out;
1164e6670feSJoseph Chen 	}
1174e6670feSJoseph Chen 
1184e6670feSJoseph Chen 	err = gpio_irq_chip->irq_init();
1194e6670feSJoseph Chen 	if (err) {
1204e6670feSJoseph Chen 		printf("ERROR: gpio interrupt init failed\n");
1214e6670feSJoseph Chen 		goto out;
1224e6670feSJoseph Chen 	}
1234e6670feSJoseph Chen 
1244e6670feSJoseph Chen 	return 0;
1254e6670feSJoseph Chen 
1264e6670feSJoseph Chen out:
1274e6670feSJoseph Chen 	initialized = false;
1284e6670feSJoseph Chen 
1294e6670feSJoseph Chen 	return err;
1304e6670feSJoseph Chen }
1314e6670feSJoseph Chen 
1324e6670feSJoseph Chen int irq_handler_enable(int irq)
1334e6670feSJoseph Chen {
1344e6670feSJoseph Chen 	if (irq_bad(irq))
1354e6670feSJoseph Chen 		return -EINVAL;
1364e6670feSJoseph Chen 
1374e6670feSJoseph Chen 	if (irq < PLATFORM_GIC_IRQS_NR)
1384e6670feSJoseph Chen 		return gic_irq_chip->irq_enable(irq);
1394e6670feSJoseph Chen 	else
1404e6670feSJoseph Chen 		return gpio_irq_chip->irq_enable(irq);
1414e6670feSJoseph Chen }
1424e6670feSJoseph Chen 
1434e6670feSJoseph Chen int irq_handler_disable(int irq)
1444e6670feSJoseph Chen {
1454e6670feSJoseph Chen 	if (irq_bad(irq))
1464e6670feSJoseph Chen 		return -EINVAL;
1474e6670feSJoseph Chen 
1484e6670feSJoseph Chen 	if (irq < PLATFORM_GIC_IRQS_NR)
1494e6670feSJoseph Chen 		return gic_irq_chip->irq_disable(irq);
1504e6670feSJoseph Chen 	else
1514e6670feSJoseph Chen 		return gpio_irq_chip->irq_disable(irq);
1524e6670feSJoseph Chen }
1534e6670feSJoseph Chen 
1544e6670feSJoseph Chen int irq_set_irq_type(int irq, unsigned int type)
1554e6670feSJoseph Chen {
1564e6670feSJoseph Chen 	if (irq_bad(irq))
1574e6670feSJoseph Chen 		return -EINVAL;
1584e6670feSJoseph Chen 
1594e6670feSJoseph Chen 	if (irq < PLATFORM_GIC_IRQS_NR)
1604e6670feSJoseph Chen 		return gic_irq_chip->irq_set_type(irq, type);
1614e6670feSJoseph Chen 	else
1624e6670feSJoseph Chen 		return gpio_irq_chip->irq_set_type(irq, type);
1634e6670feSJoseph Chen }
1644e6670feSJoseph Chen 
1654e6670feSJoseph Chen void irq_install_handler(int irq, interrupt_handler_t *handler, void *data)
1664e6670feSJoseph Chen {
1674e6670feSJoseph Chen 	if (irq_bad(irq))
1684e6670feSJoseph Chen 		return;
1694e6670feSJoseph Chen 
1704e6670feSJoseph Chen 	irqs_desc[irq].handle_irq = handler;
17142865eb5SJoseph Chen 	irqs_desc[irq].data = data;
1724e6670feSJoseph Chen }
1734e6670feSJoseph Chen 
1744e6670feSJoseph Chen void irq_free_handler(int irq)
1754e6670feSJoseph Chen {
1760e508c4fSJoseph Chen 	if (irq_handler_disable(irq))
1774e6670feSJoseph Chen 		return;
1784e6670feSJoseph Chen 
1794e6670feSJoseph Chen 	irqs_desc[irq].handle_irq = NULL;
18042865eb5SJoseph Chen 	irqs_desc[irq].data = NULL;
1814e6670feSJoseph Chen }
1824e6670feSJoseph Chen 
183*ed837edfSJoseph Chen int irqs_suspend(void)
184*ed837edfSJoseph Chen {
185*ed837edfSJoseph Chen 	int err;
186*ed837edfSJoseph Chen 
187*ed837edfSJoseph Chen 	err = gic_irq_chip->irq_suspend();
188*ed837edfSJoseph Chen 	if (err) {
189*ed837edfSJoseph Chen 		printf("ERROR: irqs suspend failed\n");
190*ed837edfSJoseph Chen 		return err;
191*ed837edfSJoseph Chen 	}
192*ed837edfSJoseph Chen 
193*ed837edfSJoseph Chen 	return 0;
194*ed837edfSJoseph Chen }
195*ed837edfSJoseph Chen 
196*ed837edfSJoseph Chen int irqs_resume(void)
197*ed837edfSJoseph Chen {
198*ed837edfSJoseph Chen 	int err;
199*ed837edfSJoseph Chen 
200*ed837edfSJoseph Chen 	err = gic_irq_chip->irq_resume();
201*ed837edfSJoseph Chen 	if (err) {
202*ed837edfSJoseph Chen 		printf("ERROR: irqs resume failed\n");
203*ed837edfSJoseph Chen 		return err;
204*ed837edfSJoseph Chen 	}
205*ed837edfSJoseph Chen 
206*ed837edfSJoseph Chen 	return 0;
207*ed837edfSJoseph Chen }
208*ed837edfSJoseph Chen 
2094e6670feSJoseph Chen #ifdef CONFIG_ARM64
2104e6670feSJoseph Chen static void cpu_local_irq_enable(void)
2114e6670feSJoseph Chen {
2124e6670feSJoseph Chen 	asm volatile("msr daifclr, #0x02");
2134e6670feSJoseph Chen }
2144e6670feSJoseph Chen 
2154e6670feSJoseph Chen static int cpu_local_irq_disable(void)
2164e6670feSJoseph Chen {
2174e6670feSJoseph Chen 	asm volatile("msr daifset, #0x02");
2184e6670feSJoseph Chen 
2194e6670feSJoseph Chen 	return 0;
2204e6670feSJoseph Chen }
2214e6670feSJoseph Chen 
2224e6670feSJoseph Chen void do_irq(struct pt_regs *pt_regs, unsigned int esr)
2234e6670feSJoseph Chen {
2244e6670feSJoseph Chen 	_do_generic_irq_handler();
2254e6670feSJoseph Chen }
2264e6670feSJoseph Chen #else
2274e6670feSJoseph Chen static void cpu_local_irq_enable(void)
2284e6670feSJoseph Chen {
2294e6670feSJoseph Chen 	unsigned long cpsr;
2304e6670feSJoseph Chen 
2314e6670feSJoseph Chen 	__asm__ __volatile__("mrs %0, cpsr\n"
2324e6670feSJoseph Chen 			     "bic %0, %0, #0x80\n"
2334e6670feSJoseph Chen 			     "msr cpsr_c, %0"
2344e6670feSJoseph Chen 			     : "=r" (cpsr) : : "memory");
2354e6670feSJoseph Chen }
2364e6670feSJoseph Chen 
2374e6670feSJoseph Chen static int cpu_local_irq_disable(void)
2384e6670feSJoseph Chen {
2394e6670feSJoseph Chen 	unsigned long old_cpsr, new_cpsr;
2404e6670feSJoseph Chen 
2414e6670feSJoseph Chen 	__asm__ __volatile__("mrs %0, cpsr\n"
2424e6670feSJoseph Chen 			     "orr %1, %0, #0xc0\n"
2434e6670feSJoseph Chen 			     "msr cpsr_c, %1"
2444e6670feSJoseph Chen 			     : "=r" (old_cpsr), "=r" (new_cpsr)
2454e6670feSJoseph Chen 			     :
2464e6670feSJoseph Chen 			     : "memory");
2474e6670feSJoseph Chen 
2484e6670feSJoseph Chen 	return (old_cpsr & 0x80) == 0;
2494e6670feSJoseph Chen }
2504e6670feSJoseph Chen 
2514e6670feSJoseph Chen void do_irq(struct pt_regs *pt_regs)
2524e6670feSJoseph Chen {
2534e6670feSJoseph Chen 	_do_generic_irq_handler();
2544e6670feSJoseph Chen }
2554e6670feSJoseph Chen #endif
2564e6670feSJoseph Chen 
2574e6670feSJoseph Chen int arch_interrupt_init(void)
2584e6670feSJoseph Chen {
2594e6670feSJoseph Chen #ifndef CONFIG_ARM64
2604e6670feSJoseph Chen 	unsigned long cpsr __maybe_unused;
2614e6670feSJoseph Chen 
2624e6670feSJoseph Chen 	/* stack has been reserved in: arch_reserve_stacks() */
2634e6670feSJoseph Chen 	IRQ_STACK_START = gd->irq_sp;
2644e6670feSJoseph Chen 
2654e6670feSJoseph Chen 	__asm__ __volatile__("mrs %0, cpsr\n"
2664e6670feSJoseph Chen 			     : "=r" (cpsr)
2674e6670feSJoseph Chen 			     :
2684e6670feSJoseph Chen 			     : "memory");
2694e6670feSJoseph Chen 
2704e6670feSJoseph Chen 	__asm__ __volatile__("msr cpsr_c, %0\n"
2714e6670feSJoseph Chen 			     "mov sp, %1\n"
2724e6670feSJoseph Chen 			     :
2734e6670feSJoseph Chen 			     : "r" (IRQ_MODE | I_BIT |
2744e6670feSJoseph Chen 				    F_BIT | (cpsr & ~FIQ_MODE)),
2754e6670feSJoseph Chen 			       "r" (IRQ_STACK_START)
2764e6670feSJoseph Chen 			     : "memory");
2774e6670feSJoseph Chen 
2784e6670feSJoseph Chen 	__asm__ __volatile__("msr cpsr_c, %0"
2794e6670feSJoseph Chen 			     :
2804e6670feSJoseph Chen 			     : "r" (cpsr)
2814e6670feSJoseph Chen 			     : "memory");
2824e6670feSJoseph Chen #endif
2834e6670feSJoseph Chen 	return _do_arch_irq_init();
2844e6670feSJoseph Chen }
2854e6670feSJoseph Chen 
2864e6670feSJoseph Chen int interrupt_init(void)
2874e6670feSJoseph Chen {
2884e6670feSJoseph Chen 	return arch_interrupt_init();
2894e6670feSJoseph Chen }
2904e6670feSJoseph Chen 
2914e6670feSJoseph Chen void enable_interrupts(void)
2924e6670feSJoseph Chen {
2934e6670feSJoseph Chen 	cpu_local_irq_enable();
2944e6670feSJoseph Chen }
2954e6670feSJoseph Chen 
2964e6670feSJoseph Chen int disable_interrupts(void)
2974e6670feSJoseph Chen {
2984e6670feSJoseph Chen 	return cpu_local_irq_disable();
2994e6670feSJoseph Chen }
300