// SPDX-License-Identifier: GPL-2.0+ #include #define CLIENT_FIQ_PRIO FIQ_PRIO_MONITOR #include #include #include #include #include #include #include #include #include #include #include "rte_gic.h" struct irq_domain *IRQ_DOMAIN; static uint32_t MAX_INTERRUPTS; static inline uint32_t read_gic_pin(int pinNr, int regtype, int bits_to_use, int atomic) { uint32_t reg = regtype + reg_offset(pinNr, bits_to_use); uint32_t val; val = rte_gicd_read(reg); val >>= to_shift(pinNr, bits_to_use); val &= group_mask(bits_to_use); return val; } static inline void write_en_gic_pin(int pinNr, int regtype, int bits_to_use, unsigned int val, int is_atomic) { uint32_t reg = regtype + reg_offset(pinNr, bits_to_use); val &= group_mask(bits_to_use); val <<= to_shift(pinNr, bits_to_use); rte_gicd_write(reg, val); } static inline void write_gic_pin(int pinNr, int regtype, int bits_to_use, unsigned int val, int is_atomic) { unsigned long flags = 0; uint32_t reg = regtype + reg_offset(pinNr, bits_to_use); uint32_t shift = to_shift(pinNr, bits_to_use); uint32_t mask = group_mask(bits_to_use) << shift; uint32_t temp; if (!is_atomic) { if (IS_ENABLED(CONFIG_AVM_FASTIRQ_TZ)) raw_spin_lock(&irq_controller_lock); else firq_raw_spin_lock_irqsave(&irq_controller_lock, flags); } temp = rte_gicd_read(reg); temp &= ~mask; temp |= (val << shift) & mask; rte_gicd_write(reg, temp); dmb(); if (!is_atomic) { if (IS_ENABLED(CONFIG_AVM_FASTIRQ_TZ)) raw_spin_unlock(&irq_controller_lock); else firq_raw_spin_unlock_irqrestore(&irq_controller_lock, flags); } } /*------------------------------------------------------------------------------------------*/ /* Registerzugriffe */ // ENABLE CLEAR unsigned int get_ICDICER(unsigned int pinNr, gic_reg_atomic_t is_atomic) { return read_gic_pin(pinNr, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, OFFSET_ICDISR, _SECURE_BITS_, val, is_atomic); } /* CPU-INTERFACE */ // PRIORITY MASK REGISTER unsigned int get_ICCPMR(gic_reg_atomic_t is_atomic) { if (!rte_gic_lower_ready()) return 0xF0; return rte_gicc_read(OFFSET_ICCPMR); } // ICCPMR void set_ICCPMR(unsigned int val, gic_reg_atomic_t is_atomic) { if (!rte_gic_lower_ready()) return; rte_gicc_write(OFFSET_ICCPMR, val); } // HIGHEST PENDING PRIORITY unsigned int get_ICCHPIR(gic_reg_atomic_t is_atomic) { if (!rte_gic_lower_ready()) return 0; return rte_gicc_read(OFFSET_ICCHPIR); } // Acknowledge unsigned int get_ICCIAR(gic_reg_atomic_t is_atomic) { if (!rte_gic_lower_ready()) return 0; return rte_gicc_read(OFFSET_ICCIAR); } // EOI void set_ICCEOIR(unsigned int val, gic_reg_atomic_t is_atomic) { if (!rte_gic_lower_ready()) return; rte_gicc_write(OFFSET_ICCEOIR, val); } // CPU CTRL void set_ICCICR(unsigned int val) { if (!rte_gic_lower_ready()) return; rte_gicc_write(0, val); } /** */ void avm_gic_fiq_enable(unsigned int pinNr, unsigned int cpu) { pr_debug("irq=%u cpu=%u\n", pinNr, cpu); set_ICDISER(pinNr, 1, IS_ATOMIC); // udelay(5); } EXPORT_SYMBOL(avm_gic_fiq_enable); void avm_gic_fiq_disable(unsigned int pinNr, unsigned int cpu) { pr_debug("irq=%u cpu=%u\n", pinNr, cpu); set_ICDICER(pinNr, 1, IS_ATOMIC); // udelay(5); } EXPORT_SYMBOL(avm_gic_fiq_disable); /** */ int avm_gic_fiq_is(unsigned int pinNr) { if (!rte_gic_lower_ready()) return 0; return get_ICDISER(pinNr, IS_ATOMIC) && !get_ICDISR(pinNr, IS_ATOMIC); } /** */ void avm_gic_fiq_configure(unsigned int pinNr, const struct cpumask *cpumask, unsigned int prio, unsigned int config, unsigned int mode) { unsigned long flags; unsigned int cpu, val = 0; raw_spin_lock_irqsave(&irq_controller_lock, flags); pr_debug("irq=%u cpumask=%*pbl prio=%u config=%u mode=%u\n", pinNr, cpumask_pr_args(cpumask), prio, config, mode); set_ICDICER(pinNr, 1, IS_ATOMIC); udelay(5); for_each_cpu(cpu, cpumask) val = cpumask_bits(cpumask)[0]; set_ICDIPTR(pinNr, val, 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); } /** */ void avm_gic_fiq_raise_irq(unsigned int pinNr) { pr_debug("irq=%u\n", pinNr); set_ICDISPR(pinNr, 1, IS_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_ATOMIC); } unsigned int avm_gic_fiq_nr_ints(void) { return MAX_INTERRUPTS; } 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)) continue; 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)) continue; // Do not disable interrupts needed for panic/watchdog handling if (i == AVM_IRQ_WD || i == AVM_IRQ_WD_BITE || i == AVM_IRQ_SWD_BARK || i < 15) continue; /* 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); } } void avm_gic_fiq_init(void) { unsigned int gic_irqs; rte_init_gic_lower(); gic_irqs = rte_gicd_read(OFFSET_ICDICTR) & 0x1f; IRQ_DOMAIN = get_irq_domain(); MAX_INTERRUPTS = (gic_irqs + 1) * 32; #ifdef CONFIG_AVM_FASTIRQ_TZ reprioritize_TZ_ints(); #endif }