/* * * Copyright (C) 2016 AVM GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #define CLIENT_FIQ_PRIO FIQ_PRIO_MONITOR #include #include #include #include #include #include #include #include #include /*--- #define DBG_TRC(args...) pr_info( args) ---*/ #define DBG_TRC(args...) no_printk(args) // Externer GIC-Lock extern raw_spinlock_t irq_controller_lock; extern void __iomem *get_dist_base(unsigned int gic_nr); extern void __iomem *get_cpu_base(unsigned int gic_nr); extern struct irq_domain *get_irq_domain(void); void __iomem *DIST_BASE; void __iomem *CPU_BASE; struct irq_domain *IRQ_DOMAIN; static uint32_t MAX_INTERRUPTS; static uint32_t rt_nr_offset; #define _ENABLE_SET_ 1 #define _ENABLE_CLEAR_ 1 #define _PENDING_SET_ 1 #define _PENDING_CLEAR_ 1 #define _TARGET_BITS_ 8 #define _PRIORITY_BITS_ 8 #define _CONFIG_BITS_ 2 #define _SECURE_BITS_ 1 #define groups_per_word(bits_per_group) (32 / bits_per_group) #define reg_offset(pinNr, bits_per_group) ((pinNr / (groups_per_word(bits_per_group))) << 2) #define group(pinNr, bits_per_group) (pinNr & ((groups_per_word(bits_per_group)) - 1)) #define to_shift(pinNr, bits_per_group) ((group(pinNr, bits_per_group)) * bits_per_group) #define group_mask(bits_per_group) ((1 << bits_per_group) - 1) #ifdef CONFIG_AVM_FASTIRQ_TZ #include #include #include #include #include #include #if 0 uint32_t smc_get_task_state(tz_task_state *state) { register u32 r0 asm("r0") = AVM_SMC_GET_TASK_STATE; register u32 r1 asm("r1") = virt_to_phys(state); asm volatile( __asmeq("%0", "r0") __asmeq("%1", "r1") ".arch_extension sec\n" "smc #0 @ switch to secure world\n" : "=r" (r0), "=r" (r1) : "r" (r0), "r" (r1) ); return r0; } #endif uint32_t smc_gicc_read(uint32_t offset) { register u32 r0 asm("r0") = AVM_SMC_GICC_READ; register u32 r1 asm("r1") = offset; asm volatile( __asmeq("%0", "r0") __asmeq("%1", "r1") ".arch_extension sec\n" "smc #0 @ switch to secure world\n" : "=r" (r0), "=r" (r1) : "r" (r0), "r" (r1) : "r2", "r3", "cc" ); return r0; } /** */ uint32_t smc_gicd_read(uint32_t offset) { register u32 r0 asm("r0") = AVM_SMC_GICD_READ; register u32 r1 asm("r1") = offset; asm volatile( __asmeq("%0", "r0") __asmeq("%1", "r1") ".arch_extension sec\n" "smc #0 @ switch to secure world\n" : "=r" (r0), "=r" (r1) : "r" (r0), "r" (r1) : "r2", "r3", "cc" ); return r0; } /** */ void smc_gicc_write(uint32_t offset, uint32_t data) { register u32 r0 asm("r0") = AVM_SMC_GICC_WRITE; register u32 r1 asm("r1") = offset; register u32 r2 asm("r2") = data; asm volatile( __asmeq("%0", "r0") __asmeq("%1", "r1") __asmeq("%2", "r2") ".arch_extension sec\n" "smc #0 @ switch to secure world\n" : "=r" (r0), "=r" (r1), "=r" (r2) : "r" (r0), "r" (r1), "r" (r2) : "r3", "cc" ); } /** */ void smc_gicd_write(uint32_t offset, uint32_t data) { register u32 r0 asm("r0") = AVM_SMC_GICD_WRITE; register u32 r1 asm("r1") = offset; register u32 r2 asm("r2") = data; asm volatile( __asmeq("%0", "r0") __asmeq("%1", "r1") __asmeq("%2", "r2") ".arch_extension sec\n" "smc #0 @ switch to secure world\n" : "=r" (r0), "=r" (r1), "=r" (r2) : "r" (r0), "r" (r1), "r" (r2) : "r3", "cc" ); } #define read_gic_pin(pinNr, regtype, bits_to_use, atomic) \ ((smc_gicd_read((u32)(regtype + reg_offset(pinNr, bits_to_use))) & \ ((group_mask(bits_to_use)) << (to_shift(pinNr, bits_to_use)))) >> (to_shift(pinNr, bits_to_use))) \ #define write_en_gic_pin(pinNr, regtype, bits_to_use, val, is_atomic) \ (smc_gicd_write((u32)(regtype + reg_offset(pinNr, bits_to_use)), \ ((val & (group_mask(bits_to_use))) << (to_shift(pinNr, bits_to_use))))) #define write_gic_pin(pinNr, regtype, bits_to_use, val, is_atomic) \ do { \ if (!is_atomic) \ raw_spin_lock(&irq_controller_lock); \ \ smc_gicd_write((u32)(regtype + reg_offset(pinNr, bits_to_use)), \ (smc_gicd_read((u32)(regtype + reg_offset(pinNr, bits_to_use))) & \ ~((group_mask(bits_to_use)) << (to_shift(pinNr, bits_to_use)))) | \ ((val & (group_mask(bits_to_use))) << (to_shift(pinNr, bits_to_use)))); \ if (!is_atomic) \ raw_spin_unlock(&irq_controller_lock); \ } while (0) #else /*--- #ifdef CONFIG_AVM_FASTIRQ_TZ ---*/ #define read_gic_pin(pinNr, regbase, bits_to_use, atomic) \ ((readl_relaxed(regbase + reg_offset(pinNr, bits_to_use)) & \ ((group_mask(bits_to_use)) << (to_shift(pinNr, bits_to_use)))) >> (to_shift(pinNr, bits_to_use))) #define write_en_gic_pin(pinNr, regbase, bits_to_use, val, is_atomic) \ do { \ (writel_relaxed(((val & (group_mask(bits_to_use))) << (to_shift(pinNr, bits_to_use))), \ (regbase + reg_offset(pinNr, bits_to_use)))); \ dmb(); \ } while (0) #define write_gic_pin(pinNr, regbase, bits_to_use, val, is_atomic) \ { \ unsigned long flags = 0; \ \ if (!is_atomic) { \ firq_raw_spin_lock_irqsave(&irq_controller_lock, flags); \ } \ writel_relaxed((readl_relaxed(regbase + reg_offset(pinNr, bits_to_use)) & \ ~((group_mask(bits_to_use)) << (to_shift(pinNr, bits_to_use)))) | \ ((val & (group_mask(bits_to_use))) << (to_shift(pinNr, bits_to_use))), \ (regbase + reg_offset(pinNr, bits_to_use))); \ dmb(); \ if (!is_atomic) { \ firq_raw_spin_unlock_irqrestore(&irq_controller_lock, flags); \ } \ } \ #endif /*--- #else #ifdef CONFIG_AVM_FASTIRQ_TZ ---*/ /*------------------------------------------------------------------------------------------*/ /* Registerzugriffe */ // ENABLE CLEAR unsigned int get_ICDICER(unsigned int pinNr, gic_reg_atomic_t is_atomic) { return read_gic_pin(pinNr, DIST_BASE + OFFSET_ICDICER, _ENABLE_CLEAR_, is_atomic); } void set_ICDICER(unsigned int pinNr, unsigned int val, gic_reg_atomic_t is_atomic) { write_en_gic_pin(pinNr, DIST_BASE + OFFSET_ICDICER, _ENABLE_CLEAR_, val, is_atomic); } // ENABLE SET unsigned int get_ICDISER(unsigned int pinNr, gic_reg_atomic_t is_atomic) { return read_gic_pin(pinNr, DIST_BASE + OFFSET_ICDISER, _ENABLE_SET_, is_atomic); } void set_ICDISER(unsigned int pinNr, unsigned int val, gic_reg_atomic_t is_atomic) { write_en_gic_pin(pinNr, DIST_BASE + OFFSET_ICDISER, _ENABLE_SET_, val, is_atomic); } // PENDING CLEAR unsigned int get_ICDICPR(unsigned int pinNr, gic_reg_atomic_t is_atomic) { return read_gic_pin(pinNr, DIST_BASE + OFFSET_ICDICPR, _PENDING_CLEAR_, is_atomic); } void set_ICDICPR(unsigned int pinNr, unsigned int val, gic_reg_atomic_t is_atomic) { write_en_gic_pin(pinNr, DIST_BASE + OFFSET_ICDICPR, _PENDING_CLEAR_, val, is_atomic); } // PENDING SET unsigned int get_ICDISPR(unsigned int pinNr, gic_reg_atomic_t is_atomic) { return read_gic_pin(pinNr, DIST_BASE + OFFSET_ICDISPR, _PENDING_SET_, is_atomic); } void set_ICDISPR(unsigned int pinNr, unsigned int val, gic_reg_atomic_t is_atomic) { write_en_gic_pin(pinNr, DIST_BASE + OFFSET_ICDISPR, _PENDING_SET_, val, is_atomic); } // TARGET unsigned int get_ICDIPTR(unsigned int pinNr, gic_reg_atomic_t is_atomic) { return read_gic_pin(pinNr, DIST_BASE + OFFSET_ICDIPTR, _TARGET_BITS_, is_atomic); } void set_ICDIPTR(unsigned int pinNr, unsigned int val, gic_reg_atomic_t is_atomic) { write_gic_pin(pinNr, DIST_BASE + OFFSET_ICDIPTR, _TARGET_BITS_, val, is_atomic); } EXPORT_SYMBOL(set_ICDIPTR); // PRIORITY unsigned int get_ICDIPR(unsigned int pinNr, gic_reg_atomic_t is_atomic) { return read_gic_pin(pinNr, DIST_BASE + OFFSET_ICDIPR, _PRIORITY_BITS_, is_atomic); } void set_ICDIPR(unsigned int pinNr, unsigned int val, gic_reg_atomic_t is_atomic) { write_gic_pin(pinNr, DIST_BASE + OFFSET_ICDIPR, _PRIORITY_BITS_, val, is_atomic); } // CONFIG unsigned int get_ICDICR(unsigned int pinNr, gic_reg_atomic_t is_atomic) { return read_gic_pin(pinNr, DIST_BASE + OFFSET_ICDICR, _CONFIG_BITS_, is_atomic); } void set_ICDICR(unsigned int pinNr, unsigned int val, gic_reg_atomic_t is_atomic) { write_gic_pin(pinNr, DIST_BASE + OFFSET_ICDICR, _CONFIG_BITS_, val, is_atomic); } // SECURITY unsigned int get_ICDISR(unsigned int pinNr, gic_reg_atomic_t is_atomic) { return read_gic_pin(pinNr, DIST_BASE + OFFSET_ICDISR, _SECURE_BITS_, is_atomic); } void set_ICDISR(unsigned int pinNr, unsigned int val, gic_reg_atomic_t is_atomic) { write_gic_pin(pinNr, DIST_BASE + OFFSET_ICDISR, _SECURE_BITS_, val, is_atomic); } /* CPU-INTERFACE */ // PRIORITY MASK REGISTER unsigned int get_ICCPMR(gic_reg_atomic_t is_atomic) { #ifndef CONFIG_AVM_FASTIRQ_TZ if (CPU_BASE) #endif { #ifdef CONFIG_AVM_FASTIRQ_TZ unsigned int ret = smc_gicc_read(OFFSET_ICCPMR); #else unsigned int ret = readl_relaxed(CPU_BASE + OFFSET_ICCPMR); dmb(); #endif return ret; } return 0xF0; } // ICCPMR void set_ICCPMR(unsigned int val, gic_reg_atomic_t is_atomic) { #ifndef CONFIG_AVM_FASTIRQ_TZ if (CPU_BASE) #endif { #ifdef CONFIG_AVM_FASTIRQ_TZ smc_gicc_write(OFFSET_ICCPMR, val); #else writel_relaxed(val, CPU_BASE + OFFSET_ICCPMR); dmb(); #endif return; } } // HIGHEST PENDING PRIORITY unsigned int get_ICCHPIR(gic_reg_atomic_t is_atomic) { #ifndef CONFIG_AVM_FASTIRQ_TZ if (CPU_BASE) #endif { #ifdef CONFIG_AVM_FASTIRQ_TZ unsigned int ret = smc_gicc_read(OFFSET_ICCHPIR); #else unsigned int ret = readl_relaxed(CPU_BASE + OFFSET_ICCHPIR); dmb(); #endif return ret; } return 0; } // Acknowledge unsigned int get_ICCIAR(gic_reg_atomic_t is_atomic) { #ifndef CONFIG_AVM_FASTIRQ_TZ if (CPU_BASE) #endif { #ifdef CONFIG_AVM_FASTIRQ_TZ unsigned int ret = smc_gicc_read(OFFSET_ICCIAR); #else unsigned int ret = readl_relaxed(CPU_BASE + OFFSET_ICCIAR); dmb(); #endif return ret; } return 0; } // EOI void set_ICCEOIR(unsigned int val, gic_reg_atomic_t is_atomic) { #ifndef CONFIG_AVM_FASTIRQ_TZ if (CPU_BASE) #endif { #ifdef CONFIG_AVM_FASTIRQ_TZ smc_gicc_write(OFFSET_ICCEOIR, val); #else writel_relaxed(val, CPU_BASE + OFFSET_ICCEOIR); dmb(); #endif } } //CPU CTRL void set_ICCICR(unsigned int val) { #ifndef CONFIG_AVM_FASTIRQ_TZ if (CPU_BASE) #endif { #ifdef CONFIG_AVM_FASTIRQ_TZ smc_gicc_write(0, val); #else writel_relaxed(val, CPU_BASE); dmb(); #endif } } /** */ void avm_gic_fiq_enable(unsigned int pinNr, unsigned int cpu) { DBG_TRC("%s: irq=%u cpu=%u\n", __func__, pinNr, cpu); set_ICDISER(pinNr, 1, IS_NOT_ATOMIC); //udelay(5); } EXPORT_SYMBOL(avm_gic_fiq_enable); void avm_gic_fiq_disable(unsigned int pinNr, unsigned int cpu) { DBG_TRC("%s: irq=%u cpu=%u\n", __func__, pinNr, cpu); set_ICDICER(pinNr, 1, IS_NOT_ATOMIC); //udelay(5); } EXPORT_SYMBOL(avm_gic_fiq_disable); /** */ int avm_gic_fiq_is(unsigned int pinNr) { unsigned int is_fiq = 0; #ifndef CONFIG_AVM_FASTIRQ_TZ if (DIST_BASE) #endif { /*--- unsigned long flags; ---*/ /*--- raw_spin_lock_irqsave(&irq_controller_lock, flags); ---*/ is_fiq = get_ICDISER(pinNr, IS_ATOMIC) && !get_ICDISR(pinNr, IS_ATOMIC); /*--- raw_spin_unlock_irqrestore(&irq_controller_lock, flags); ---*/ } return is_fiq; } /** */ void avm_gic_fiq_setup(unsigned int pinNr, unsigned int cpumask, unsigned int prio, unsigned int config, unsigned int mode) { unsigned long flags; raw_spin_lock_irqsave(&irq_controller_lock, flags); DBG_TRC("%s: irq=%u cpumask=%u prio=%u config=%u mode=%u\n", __func__, pinNr, cpumask, prio, config, mode); set_ICDICER(pinNr, 1, IS_ATOMIC); udelay(5); set_ICDIPTR(pinNr, cpumask, IS_ATOMIC); set_ICDIPR(pinNr, prio, IS_ATOMIC); set_ICDICR(pinNr, config, IS_ATOMIC); set_ICDISR(pinNr, mode, IS_ATOMIC); udelay(5); set_ICDISER(pinNr, 1, IS_ATOMIC); raw_spin_unlock_irqrestore(&irq_controller_lock, flags); } EXPORT_SYMBOL(avm_gic_fiq_setup); /** */ void avm_gic_fiq_raise_irq(unsigned int pinNr) { DBG_TRC("%s: irq=%u\n", __func__, pinNr); set_ICDISPR(pinNr, 1, IS_NOT_ATOMIC); } EXPORT_SYMBOL(avm_gic_fiq_raise_irq); /** */ void avm_gic_clearpending_irq(unsigned int pinNr) { set_ICDICPR(pinNr, 1, IS_ATOMIC); } unsigned int avm_gic_fiq_get_ICCHPIR(void) { return get_ICCHPIR(IS_NOT_ATOMIC); } unsigned int avm_gic_fiq_nr_ints(void) { return MAX_INTERRUPTS; } void __iomem *avm_gic_fiq_dist_base(void) { return DIST_BASE; } void __iomem *avm_gic_fiq_cpu_base(void) { return CPU_BASE; } unsigned int avm_gic_fiq_virq(unsigned int hwirq) { return irq_find_mapping(IRQ_DOMAIN, hwirq); } static void reprioritize_TZ_ints(void) { unsigned int i; for (i = 0; i < avm_gic_fiq_nr_ints(); i++) { if (get_ICDISR(i, IS_NOT_ATOMIC) == 0) { set_ICDIPR(i, 0x60, IS_NOT_ATOMIC); pr_info("TZ-pin nr %d reprioritized to %x\n", i, 0x60); } } } void reinit_GIC(void) { unsigned int i; for (i = 0; i < avm_gic_fiq_nr_ints(); i++) { if (get_ICDISER(i, IS_NOT_ATOMIC) == 1) { if ((i != AVM_IRQ_WD) && (i != AVM_IRQ_WD_BITE) && (i != AVM_IRQ_SWD_BARK) && (i < AVM_IRQ_MONITOR_START)) { /* Disable Pin */ set_ICDICER(i, 1, IS_NOT_ATOMIC); /* Interrupt Pending löschen */ set_ICDICPR(i, 1, IS_NOT_ATOMIC); //pr_info("GIC-pin nr %d switched off!\n", i); } } } } uint32_t get_rt_nr_offset(void) { return rt_nr_offset; } static uint32_t parse_rt_nr_offset(void) { struct device_node *np; int i, count; uint32_t val, last_val; np = of_find_compatible_node(NULL, NULL, "avm,rt_framework"); if (np) { count = of_property_count_u32_elems(np, "monitor"); if (count < num_possible_cpus()) { pr_err("[avm_gic] RT Framework: Invalid interrupt configuration; exceeding cpu count.\n"); goto error; } for (i = 0; i < count; i++) { of_property_read_u32_index(np, "monitor", i, &val); if (i != 0) { if (last_val + 1 != val) { pr_err("[avm_gic] RT Framework: Invalid interrupt numbers; not directly ascending.\n"); goto error; } } last_val = val; } count = of_property_count_u32_elems(np, "ipi"); if (count < num_possible_cpus()) { pr_err("[avm_gic] RT Framework: Invalid interrupt configuration; exceeding cpu count.\n"); goto error; } for (i = 0; i < count; i++) { of_property_read_u32_index(np, "ipi", i, &val); if (last_val + 1 != val) { pr_err("[avm_gic] RT Framework: Invalid interrupt numbers; not directly ascending.\n"); goto error; } last_val = val; } } // be consistent with the old api return val + 1; // no possible way to recover.. // Unfortunately we can not panic here if want to see any kernel boot messages error: pr_err("[avm_gic] Unable to find valid dt entry for the avm rt_framework. " "Expect more failures during boot!\n"); return 0; } void avm_gic_fiq_init(void) { unsigned int gic_irqs; #ifdef CONFIG_AVM_FASTIRQ_TZ DIST_BASE = 0; CPU_BASE = 0; gic_irqs = smc_gicd_read(OFFSET_ICDICTR) & 0x1f; #else DIST_BASE = get_dist_base(0); CPU_BASE = get_cpu_base(0); gic_irqs = readl(DIST_BASE + OFFSET_ICDICTR) & 0x1f; #endif IRQ_DOMAIN = get_irq_domain(); MAX_INTERRUPTS = (gic_irqs + 1) * 32; rt_nr_offset = parse_rt_nr_offset(); #ifdef CONFIG_AVM_FASTIRQ_TZ reprioritize_TZ_ints(); #endif }