/*------------------------------------------------------------------------------------------*\ * * 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_WATCHDOG #include #include #include #include #include #include #include #include /*--- #define DBG_TRC(args...) printk(KERN_INFO args) ---*/ #define DBG_TRC(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 ); void __iomem *DIST_BASE; void __iomem *CPU_BASE; unsigned int MAX_INTERRUPTS; #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) \ { \ 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); \ } #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) \ (writel_relaxed( ((val & (group_mask(bits_to_use))) << (to_shift(pinNr, bits_to_use))), \ (regbase + reg_offset(pinNr, bits_to_use)) )); \ dmb() \ #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); return; } // 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); return; } // 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); return; } // 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); return; } // 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); return; } // 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); return; } // 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); return; } // 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); return; } /* CPU-INTERFACE */ // PRIORITY MASK REGISTER unsigned int get_ICCPMR(gic_reg_atomic_t is_atomic) { #ifndef CONFIG_AVM_FASTIRQ_TZ if(CPU_BASE) #endif { #if CONFIG_AVM_FASTIRQ_TZ unsigned int ret = smc_gicc_read(OFFSET_ICCPMR); #else unsigned int ret = readl_relaxed(CPU_BASE + OFFSET_ICCPMR); #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 { #if CONFIG_AVM_FASTIRQ_TZ smc_gicc_write(OFFSET_ICCPMR, val); #else writel_relaxed(val, CPU_BASE + OFFSET_ICCPMR); dmb(); #endif return; } return; } // HIGHEST PENDING PRIORITY unsigned int get_ICCHPIR(gic_reg_atomic_t is_atomic) { #ifndef CONFIG_AVM_FASTIRQ_TZ if(CPU_BASE) #endif { #if CONFIG_AVM_FASTIRQ_TZ unsigned int ret = smc_gicc_read(OFFSET_ICCHPIR); #else unsigned int ret = readl_relaxed(CPU_BASE + OFFSET_ICCHPIR); #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 { #if CONFIG_AVM_FASTIRQ_TZ unsigned int ret = smc_gicc_read(OFFSET_ICCIAR); #else unsigned int ret = readl(CPU_BASE + OFFSET_ICCIAR); #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 { #if CONFIG_AVM_FASTIRQ_TZ smc_gicc_write(OFFSET_ICCEOIR, val); #else writel_relaxed(val, CPU_BASE + OFFSET_ICCEOIR); dmb(); #endif return; } return; } //CPU CTRL void set_ICCICR(unsigned int val) { #ifndef CONFIG_AVM_FASTIRQ_TZ if(CPU_BASE) #endif { #if 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); return; } 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); return; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ 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); return; } void avm_gic_fiq_raise_irq(unsigned int pinNr) { DBG_TRC("%s: irq=%u\n", __func__, pinNr); set_ICDISPR(pinNr, 1, IS_NOT_ATOMIC); } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ 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; } void avm_gic_fiq_init(void) { unsigned int gic_irqs; #if 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 MAX_INTERRUPTS = (gic_irqs + 1) * 32; }