/*------------------------------------------------------------------------------------------*\ * * 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 #include #include #include #include #ifdef CONFIG_MACH_PUMA7 #include #include #include #endif #include #include #define DBG_TRC(args...) printk(KERN_ERR args) //#define DBG_TRC(args...) #if defined(CONFIG_MACH_PUMA7) && defined(CONFIG_ARM_GIC) // Externer GIC-Lock extern raw_spinlock_t irq_controller_lock; #else #undef raw_spin_lock_irqsave #define raw_spin_lock_irqsave(lock, flags) \ raw_local_irq_save(flags) #undef raw_spin_unlock_irqsave #define raw_spin_unlock_irqsave(lock, flags) \ raw_local_irq_restore(flags) #endif 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; typedef enum gic_reg_atomic_e { IS_NOT_ATOMIC, IS_ATOMIC } gic_reg_atomic_t; #ifdef CONFIG_MACH_PUMA7 #define _ENABLE_SET_ 32 #define _ENABLE_CLEAR_ 32 #define _PENDING_SET_ 32 #define _PENDING_CLEAR_ 32 #define _TARGET_BITS_ 8 #define _PRIORITY_BITS_ 8 #define _CONFIG_BITS_ 1 #define _SECURE_BITS_ 1 #define _PENDIG_ID_BITS_ 32 #else #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 #endif #define groups_per_word(bits_per_group) (32 / bits_per_group) #define reg_offset(pinNr, bits_per_group) (((unsigned int)pinNr / (groups_per_word(bits_per_group))) << 2) #define group(pinNr, bits_per_group) ((unsigned int)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) ((unsigned int)(((unsigned long long)1 << bits_per_group) - 1)) static unsigned int read_gic_pin(unsigned int pinNr, void *regbase, unsigned int bits_to_use, gic_reg_atomic_t is_atomic) { void * address = ((void *)(regbase + reg_offset(pinNr, bits_to_use))); unsigned int value = 0; __raw_readsl( address, &value, 1 ); value = (value & ((group_mask(bits_to_use)) << (to_shift(pinNr, bits_to_use)))) >> (to_shift(pinNr, bits_to_use)); return value; } static void write_en_gic_pin(unsigned int pinNr, void *regbase, unsigned int bits_to_use, unsigned int val, gic_reg_atomic_t is_atomic) { void * address = ((void *)(regbase + reg_offset(pinNr, bits_to_use))); unsigned int value = (val & (group_mask(bits_to_use))) << (to_shift(pinNr, bits_to_use)); __raw_writesl( address, &value, 1 ); } static void write_gic_pin(unsigned int pinNr, void *regbase, unsigned int bits_to_use, unsigned int val, gic_reg_atomic_t is_atomic) { unsigned long flags; if(!is_atomic) raw_spin_lock_irqsave(&irq_controller_lock, flags); { void * address = ((void *)(regbase + reg_offset(pinNr, bits_to_use))); unsigned int value = 0; __raw_readsl( address, &value, 1 ); value = (value & ~((group_mask(bits_to_use)) << (to_shift(pinNr, bits_to_use)))) | ((val & (group_mask(bits_to_use))) << (to_shift(pinNr, bits_to_use))); __raw_writesl( address, &value, 1 ); } if(!is_atomic) { #if defined(CONFIG_MACH_PUMA7) && defined(CONFIG_ARM_GIC) raw_spin_unlock_irqrestore(&irq_controller_lock, flags); #else raw_spin_unlock_irqsave(&irq_controller_lock, flags); #endif } } /*------------------------------------------------------------------------------------------*/ /* Registerzugriffe */ // ENABLE CLEAR unsigned int get_ICDICER(unsigned int pinNr, gic_reg_atomic_t is_atomic) { #ifdef CONFIG_MACH_PUMA7 return( read_gic_pin(((unsigned int)(DIST_BASE + OFFSET_ICDICER) >> 2), 0, _ENABLE_CLEAR_, is_atomic) ); #else return( read_gic_pin(pinNr, DIST_BASE + OFFSET_ICDICER, _ENABLE_CLEAR_, is_atomic) ); #endif } void set_ICDICER(unsigned int pinNr, unsigned int val, gic_reg_atomic_t is_atomic) { #ifdef CONFIG_MACH_PUMA7 write_en_gic_pin(((unsigned int)(DIST_BASE + OFFSET_ICDICER) >> 2), 0, _ENABLE_CLEAR_, pinNr, is_atomic); #else write_en_gic_pin(pinNr, DIST_BASE + OFFSET_ICDICER, _ENABLE_CLEAR_, val, is_atomic); #endif return; } // ENABLE SET unsigned int get_ICDISER(unsigned int pinNr, gic_reg_atomic_t is_atomic) { #ifdef CONFIG_MACH_PUMA7 return( read_gic_pin(((unsigned int)(DIST_BASE + OFFSET_ICDISER) >> 2), 0, _ENABLE_SET_, is_atomic) ); #else return( read_gic_pin(pinNr, DIST_BASE + OFFSET_ICDISER, _ENABLE_SET_, is_atomic) ); #endif } void set_ICDISER(unsigned int pinNr, unsigned int val, gic_reg_atomic_t is_atomic) { #ifdef CONFIG_MACH_PUMA7 write_en_gic_pin(((unsigned int)(DIST_BASE + OFFSET_ICDISER) >> 2), 0, _ENABLE_SET_, pinNr, is_atomic); #else write_en_gic_pin(pinNr, DIST_BASE + OFFSET_ICDISER, _ENABLE_SET_, val, is_atomic); #endif return; } // PENDING CLEAR unsigned int get_ICDICPR(unsigned int pinNr, gic_reg_atomic_t is_atomic) { #ifdef CONFIG_MACH_PUMA7 return( read_gic_pin(((unsigned int)(DIST_BASE + OFFSET_ICDICPR) >> 2), 0, _PENDING_CLEAR_, is_atomic) ); #else return( read_gic_pin(pinNr, DIST_BASE + OFFSET_ICDICPR, _PENDING_CLEAR_, is_atomic) ); #endif } void set_ICDICPR(unsigned int pinNr, unsigned int val, gic_reg_atomic_t is_atomic) { #ifdef CONFIG_MACH_PUMA7 write_en_gic_pin(((unsigned int)(DIST_BASE + OFFSET_ICDICPR) >> 2), 0, _PENDING_CLEAR_, pinNr, is_atomic); #else write_en_gic_pin(pinNr, DIST_BASE + OFFSET_ICDICPR, _PENDING_CLEAR_, val, is_atomic); #endif return; } // PENDING SET unsigned int get_ICDISPR(unsigned int pinNr, gic_reg_atomic_t is_atomic) { #ifdef CONFIG_MACH_PUMA7 return( read_gic_pin(((unsigned int)(DIST_BASE + OFFSET_ICDISPR) >> 2), 0, _PENDING_SET_, is_atomic) ); #else return( read_gic_pin(pinNr, DIST_BASE + OFFSET_ICDISPR, _PENDING_SET_, is_atomic) ); #endif } void set_ICDISPR(unsigned int pinNr, unsigned int val, gic_reg_atomic_t is_atomic) { #ifdef CONFIG_MACH_PUMA7 write_en_gic_pin(((unsigned int)(DIST_BASE + OFFSET_ICDISPR) >> 2), 0, _PENDING_SET_, pinNr, is_atomic); #else write_en_gic_pin(pinNr, DIST_BASE + OFFSET_ICDISPR, _PENDING_SET_, val, is_atomic); #endif return; } // TARGET / HOST INTERFACE 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 / CHANNEL 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) { DBG_TRC("%s: DIST_BASE=%x OFFSET_ICDIPR=%x pin=%x val=%x\n", __func__, (unsigned int)DIST_BASE, OFFSET_ICDIPR, pinNr, val); DBG_TRC(" groups_per_word=%x\n", groups_per_word(_PRIORITY_BITS_)); DBG_TRC(" reg_offset=%x\n", reg_offset(pinNr, _PRIORITY_BITS_)); DBG_TRC(" group=%x\n", group(pinNr, _PRIORITY_BITS_)); DBG_TRC(" to_shift=%x\n", to_shift(pinNr, _PRIORITY_BITS_)); DBG_TRC(" group_mask=%x\n", group_mask(_PRIORITY_BITS_)); write_gic_pin(pinNr, DIST_BASE + OFFSET_ICDIPR, _PRIORITY_BITS_, val, is_atomic); return; } // CONFIG / TYPE 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 / POLARITY 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 */ // HIGHEST PENDING PRIORITY unsigned int get_ICCHPIR(gic_reg_atomic_t is_atomic) { #ifdef CONFIG_MACH_PUMA7 #else if(CPU_BASE) { #endif #ifdef CONFIG_MACH_PUMA7 unsigned int idx = ( read_gic_pin(((unsigned int)(DIST_BASE + OFFSET_ICCHPIR) >> 2), 0, _PENDIG_ID_BITS_, is_atomic) ); if( idx & 0x80000000 ) return 0; return ( idx & 0x3FF ); #else return( read_gic_pin(((unsigned int)(DIST_BASE + OFFSET_ICCHPIR) >> 2), 0, _PENDIG_ID_BITS_, is_atomic) ); #endif #ifdef CONFIG_MACH_PUMA7 #else } #endif return 0; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ 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; if(DIST_BASE) { /*--- unsigned long flags; ---*/ /*--- raw_spin_lock_irqsave(&irq_controller_lock, flags); ---*/ is_fiq = get_ICDISER(pinNr, IS_ATOMIC) #ifdef CONFIG_MACH_PUMA7 && !get_ICDIPR(pinNr, IS_ATOMIC) #else && !get_ICDISR(pinNr, IS_ATOMIC) #endif ; /*--- raw_spin_unlock_irqrestore(&irq_controller_lock, flags); ---*/ } return is_fiq; } #ifdef CONFIG_MACH_PUMA7 void avm_gic_fiq_setup(unsigned int pinNr, const struct cpumask *cpumask, unsigned int chan, unsigned int type, unsigned int pol) #else void avm_gic_fiq_setup(unsigned int pinNr, const struct cpumask *cpumask, unsigned int prio, unsigned int config, unsigned int mode) #endif { unsigned long flags; raw_spin_lock_irqsave(&irq_controller_lock, flags); DBG_TRC("%s: irq=%u cpu=%*pbl prio=%u config=%u mode=%u\n", __func__, pinNr, cpumask_pr_args(cpumask), chan, type, pol); set_ICDICER(pinNr, 1, IS_ATOMIC); udelay(5); #ifdef CONFIG_MACH_PUMA7 #define prio chan #define config type #define mode pol #else set_ICDIPTR(pinNr, cpumask_bits(cpumask)[0], IS_ATOMIC); #endif 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); #if defined(CONFIG_MACH_PUMA7) && defined(CONFIG_ARM_GIC) raw_spin_unlock_irqrestore(&irq_controller_lock, flags); #else raw_spin_unlock_irqsave(&irq_controller_lock, flags); #endif #ifdef CONFIG_MACH_PUMA7 #undef prio #undef config #undef mode #endif 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); } 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; } void avm_gic_fiq_init(void) { #ifdef CONFIG_MACH_PUMA7 DIST_BASE = (void *)AVALANCHE_INTC_BASE; CPU_BASE = (void *)AVALANCHE_INTC_BASE; MAX_INTERRUPTS = NUM_MAX_SYSTEM_INTS; #else DIST_BASE = get_dist_base(0); CPU_BASE = get_cpu_base(0); { unsigned int gic_irqs; gic_irqs = readl(DIST_BASE + OFFSET_ICDICTR) & 0x1f; MAX_INTERRUPTS = (gic_irqs + 1) * 32; } #endif }