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