/* * (C) Copyright 2017 Rockchip Electronics Co., Ltd * * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #include #include "irq-gic.h" typedef enum INT_TRIG { INT_LEVEL_TRIGGER, INT_EDGE_TRIGGER } eINT_TRIG; typedef enum INT_SECURE { INT_SECURE, INT_NOSECURE } eINT_SECURE; typedef enum INT_SIGTYPE { INT_SIGTYPE_IRQ, INT_SIGTYPE_FIQ } eINT_SIGTYPE; #define g_gicd ((pGICD_REG)GICD_BASE) #define g_gicc ((pGICC_REG)GICC_BASE) __maybe_unused static u8 g_gic_cpumask = 0x01; static inline void int_set_prio_filter(u32 nprio) { g_gicc->iccpmr = (nprio & 0xff); } static inline void int_enable_distributor(void) { g_gicd->icddcr = 0x01; } static inline void int_disable_distributor(void) { g_gicd->icddcr = 0x00; } static inline void int_enable_secure_signal(void) { g_gicc->iccicr |= 0x01; } static inline void int_disable_secure_signal(void) { g_gicc->iccicr &= (~0x01); } static inline void int_enable_nosecure_signal(void) { g_gicc->iccicr |= 0x02; } static inline void int_disable_nosecure_signal(void) { g_gicc->iccicr &= (~0x02); } static int gic_irq_set_trigger(int irq, eINT_TRIG ntrig) { u32 group, offset; if (irq >= PLATFORM_GIC_IRQS_NR) return -EINVAL; group = irq / 16; offset = irq % 16; if (ntrig == INT_LEVEL_TRIGGER) g_gicd->icdicfr[group] &= (~(1 << (2 * offset + 1))); else g_gicd->icdicfr[group] |= (1 << (2 * offset + 1)); return 0; } __maybe_unused static int gic_irq_set_pending(int irq) { u32 group, offset; if (irq >= PLATFORM_GIC_IRQS_NR) return -EINVAL; group = irq / 32; offset = irq % 32; g_gicd->icdispr[group] = (0x1 << offset); return 0; } __maybe_unused static int gic_irq_clear_pending(int irq) { u32 group, offset; if (irq >= PLATFORM_GIC_IRQS_NR) return -EINVAL; group = irq / 32; offset = irq % 32; g_gicd->icdicpr[group] = (0x1 << offset); return 0; } __maybe_unused static int gic_irq_set_secure(int irq, eINT_SECURE nsecure) { u32 group, offset; if (irq >= PLATFORM_GIC_IRQS_NR) return -EINVAL; group = irq / 32; offset = irq % 32; g_gicd->icdiser[group] |= nsecure << offset; return 0; } __maybe_unused static u32 gic_get_cpumask(void) { u32 mask = 0, i; for (i = mask = 0; i < 32; i += 4) { mask = g_gicd->itargetsr[i]; mask |= mask >> 16; mask |= mask >> 8; if (mask) break; } if (!mask) printf("GIC CPU mask not found.\n"); debug("GIC CPU mask = 0x%08x\n", mask); return mask; } static int gic_irq_enable(int irq) { #ifdef CONFIG_GICV2 u32 shift = (irq % 4) * 8; u32 offset = (irq / 4); u32 M, N; if (irq >= PLATFORM_GIC_IRQS_NR) return -EINVAL; M = irq / 32; N = irq % 32; g_gicc->iccicr &= (~0x08); g_gicd->icdiser[M] = (0x1 << N); g_gicd->itargetsr[offset] &= ~(0xFF << shift); g_gicd->itargetsr[offset] |= (g_gic_cpumask << shift); #else u32 M, N; if (irq >= PLATFORM_GIC_IRQS_NR) return -EINVAL; M = irq / 32; N = irq % 32; g_gicd->icdiser[M] = (0x1 << N); #endif return 0; } static int gic_irq_disable(int irq) { u32 group, offset; if (irq >= PLATFORM_GIC_IRQS_NR) return -EINVAL; group = irq / 32; offset = irq % 32; g_gicd->icdicer[group] = (0x1 << offset); return 0; } /* * irq_set_type - set the irq trigger type for an irq * * @irq: irq number * @type: IRQ_TYPE_{LEVEL,EDGE}_* value - see asm/arch/irq.h */ static int gic_irq_set_type(int irq, unsigned int type) { unsigned int int_type; switch (type) { case IRQ_TYPE_EDGE_RISING: case IRQ_TYPE_EDGE_FALLING: int_type = INT_EDGE_TRIGGER; break; case IRQ_TYPE_LEVEL_HIGH: case IRQ_TYPE_LEVEL_LOW: int_type = INT_LEVEL_TRIGGER; break; default: return -EINVAL; } gic_irq_set_trigger(irq, int_type); return 0; } static void gic_irq_eoi(int irq) { #ifdef CONFIG_GICV2 g_gicc->icceoir = irq; #else asm volatile("msr " __stringify(ICC_EOIR1_EL1) ", %0" : : "r" ((u64)irq)); asm volatile("msr " __stringify(ICC_DIR_EL1) ", %0" : : "r" ((u64)irq)); isb(); #endif } static int gic_irq_get(void) { #ifdef CONFIG_GICV2 return g_gicc->icciar & 0x3ff; /* bit9 - bit0 */ #else u64 irqstat; asm volatile("mrs %0, " __stringify(ICC_IAR1_EL1) : "=r" (irqstat)); return (u32)irqstat & 0x3ff; #endif } static int gic_irq_init(void) { /* GICV3 done in: arch/arm/cpu/armv8/start.S */ #ifdef CONFIG_GICV2 /* end of interrupt */ g_gicc->icceoir = PLATFORM_GIC_IRQS_NR; /* disable gicc and gicd */ g_gicc->iccicr = 0x00; g_gicd->icddcr = 0x00; /* enable interrupt */ g_gicd->icdicer[0] = 0xFFFFFFFF; g_gicd->icdicer[1] = 0xFFFFFFFF; g_gicd->icdicer[2] = 0xFFFFFFFF; g_gicd->icdicer[3] = 0xFFFFFFFF; g_gicd->icdicfr[3] &= ~(1 << 1); /* set interrupt priority threhold min: 256 */ int_set_prio_filter(0xff); int_enable_secure_signal(); int_enable_nosecure_signal(); int_enable_distributor(); g_gic_cpumask = gic_get_cpumask(); #endif return 0; } static struct irq_chip gic_irq_chip = { .name = "gic-irq-chip", .irq_init = gic_irq_init, .irq_get = gic_irq_get, .irq_enable = gic_irq_enable, .irq_disable = gic_irq_disable, .irq_eoi = gic_irq_eoi, .irq_set_type = gic_irq_set_type, }; struct irq_chip *arch_gic_irq_init(void) { return &gic_irq_chip; }