| /* |
| * Copyright (c) 2012-2015 Travis Geiselbrecht |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining |
| * a copy of this software and associated documentation files |
| * (the "Software"), to deal in the Software without restriction, |
| * including without limitation the rights to use, copy, modify, merge, |
| * publish, distribute, sublicense, and/or sell copies of the Software, |
| * and to permit persons to whom the Software is furnished to do so, |
| * subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be |
| * included in all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
| * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
| * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
| * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
| * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| */ |
| #include <assert.h> |
| #include <bits.h> |
| #include <err.h> |
| #include <sys/types.h> |
| #include <debug.h> |
| #include <dev/interrupt/arm_gic.h> |
| #include <reg.h> |
| #include <kernel/thread.h> |
| #include <kernel/debug.h> |
| #include <lk/init.h> |
| #include <lk/macros.h> |
| #include <platform/interrupts.h> |
| #include <arch/ops.h> |
| #include <platform/gic.h> |
| #include <trace.h> |
| #if WITH_LIB_SM |
| #include <lib/sm.h> |
| #include <lib/sm/sm_err.h> |
| #endif |
| |
| #define LOCAL_TRACE 0 |
| |
| #if ARCH_ARM |
| #include <arch/arm.h> |
| #define iframe arm_iframe |
| #define IFRAME_PC(frame) ((frame)->pc) |
| #endif |
| #if ARCH_ARM64 |
| #include <arch/arm64.h> |
| #define iframe arm64_iframe_short |
| #define IFRAME_PC(frame) ((frame)->elr) |
| #endif |
| |
| static status_t arm_gic_set_secure_locked(u_int irq, bool secure); |
| |
| static spin_lock_t gicd_lock; |
| #if WITH_LIB_SM |
| #define GICD_LOCK_FLAGS SPIN_LOCK_FLAG_IRQ_FIQ |
| #else |
| #define GICD_LOCK_FLAGS SPIN_LOCK_FLAG_INTERRUPTS |
| #endif |
| #define GIC_MAX_PER_CPU_INT 32 |
| |
| #if WITH_LIB_SM |
| static bool arm_gic_non_secure_interrupts_frozen; |
| |
| static bool arm_gic_interrupt_change_allowed(int irq) |
| { |
| if (!arm_gic_non_secure_interrupts_frozen) |
| return true; |
| |
| TRACEF("change to interrupt %d ignored after booting ns\n", irq); |
| return false; |
| } |
| |
| static void suspend_resume_fiq(bool resume_gicc, bool resume_gicd); |
| #else |
| static bool arm_gic_interrupt_change_allowed(int irq) |
| { |
| return true; |
| } |
| |
| static void suspend_resume_fiq(bool resume_gicc, bool resume_gicd) |
| { |
| } |
| #endif |
| |
| |
| struct int_handler_struct { |
| int_handler handler; |
| void *arg; |
| }; |
| |
| static struct int_handler_struct int_handler_table_per_cpu[GIC_MAX_PER_CPU_INT][SMP_MAX_CPUS]; |
| static struct int_handler_struct int_handler_table_shared[MAX_INT-GIC_MAX_PER_CPU_INT]; |
| |
| static struct int_handler_struct *get_int_handler(unsigned int vector, uint cpu) |
| { |
| if (vector < GIC_MAX_PER_CPU_INT) |
| return &int_handler_table_per_cpu[vector][cpu]; |
| else |
| return &int_handler_table_shared[vector - GIC_MAX_PER_CPU_INT]; |
| } |
| |
| void register_int_handler(unsigned int vector, int_handler handler, void *arg) |
| { |
| struct int_handler_struct *h; |
| uint cpu = arch_curr_cpu_num(); |
| |
| spin_lock_saved_state_t state; |
| |
| if (vector >= MAX_INT) |
| panic("register_int_handler: vector out of range %d\n", vector); |
| |
| spin_lock_save(&gicd_lock, &state, GICD_LOCK_FLAGS); |
| |
| if (arm_gic_interrupt_change_allowed(vector)) { |
| h = get_int_handler(vector, cpu); |
| h->handler = handler; |
| h->arg = arg; |
| } |
| |
| spin_unlock_restore(&gicd_lock, state, GICD_LOCK_FLAGS); |
| } |
| |
| #define GICREG(gic, reg) (*REG32(GICBASE(gic) + (reg))) |
| |
| /* main cpu regs */ |
| #define GICC_CTLR (GICC_OFFSET + 0x0000) |
| #define GICC_PMR (GICC_OFFSET + 0x0004) |
| #define GICC_BPR (GICC_OFFSET + 0x0008) |
| #define GICC_IAR (GICC_OFFSET + 0x000c) |
| #define GICC_EOIR (GICC_OFFSET + 0x0010) |
| #define GICC_RPR (GICC_OFFSET + 0x0014) |
| #define GICC_HPPIR (GICC_OFFSET + 0x0018) |
| #define GICC_APBR (GICC_OFFSET + 0x001c) |
| #define GICC_AIAR (GICC_OFFSET + 0x0020) |
| #define GICC_AEOIR (GICC_OFFSET + 0x0024) |
| #define GICC_AHPPIR (GICC_OFFSET + 0x0028) |
| #define GICC_APR(n) (GICC_OFFSET + 0x00d0 + (n) * 4) |
| #define GICC_NSAPR(n) (GICC_OFFSET + 0x00e0 + (n) * 4) |
| #define GICC_IIDR (GICC_OFFSET + 0x00fc) |
| #define GICC_DIR (GICC_OFFSET + 0x1000) |
| |
| /* distribution regs */ |
| #define GICD_CTLR (GICD_OFFSET + 0x000) |
| #define GICD_TYPER (GICD_OFFSET + 0x004) |
| #define GICD_IIDR (GICD_OFFSET + 0x008) |
| #define GICD_IGROUPR(n) (GICD_OFFSET + 0x080 + (n) * 4) |
| #define GICD_ISENABLER(n) (GICD_OFFSET + 0x100 + (n) * 4) |
| #define GICD_ICENABLER(n) (GICD_OFFSET + 0x180 + (n) * 4) |
| #define GICD_ISPENDR(n) (GICD_OFFSET + 0x200 + (n) * 4) |
| #define GICD_ICPENDR(n) (GICD_OFFSET + 0x280 + (n) * 4) |
| #define GICD_ISACTIVER(n) (GICD_OFFSET + 0x300 + (n) * 4) |
| #define GICD_ICACTIVER(n) (GICD_OFFSET + 0x380 + (n) * 4) |
| #define GICD_IPRIORITYR(n) (GICD_OFFSET + 0x400 + (n) * 4) |
| #define GICD_ITARGETSR(n) (GICD_OFFSET + 0x800 + (n) * 4) |
| #define GICD_ICFGR(n) (GICD_OFFSET + 0xc00 + (n) * 4) |
| #define GICD_NSACR(n) (GICD_OFFSET + 0xe00 + (n) * 4) |
| #define GICD_SGIR (GICD_OFFSET + 0xf00) |
| #define GICD_CPENDSGIR(n) (GICD_OFFSET + 0xf10 + (n) * 4) |
| #define GICD_SPENDSGIR(n) (GICD_OFFSET + 0xf20 + (n) * 4) |
| |
| #define GIC_REG_COUNT(bit_per_reg) DIV_ROUND_UP(MAX_INT, (bit_per_reg)) |
| #define DEFINE_GIC_SHADOW_REG(name, bit_per_reg, init_val, init_from) \ |
| uint32_t (name)[GIC_REG_COUNT(bit_per_reg)] = { \ |
| [(init_from / bit_per_reg) ... \ |
| (GIC_REG_COUNT(bit_per_reg) - 1)] = (init_val) \ |
| } |
| |
| #if WITH_LIB_SM |
| static DEFINE_GIC_SHADOW_REG(gicd_igroupr, 32, ~0U, 0); |
| #endif |
| static DEFINE_GIC_SHADOW_REG(gicd_itargetsr, 4, 0x01010101, 32); |
| |
| static void gic_set_enable(uint vector, bool enable) |
| { |
| int reg = vector / 32; |
| uint32_t mask = 1ULL << (vector % 32); |
| |
| if (enable) |
| GICREG(0, GICD_ISENABLER(reg)) = mask; |
| else |
| GICREG(0, GICD_ICENABLER(reg)) = mask; |
| } |
| |
| static void arm_gic_init_percpu(uint level) |
| { |
| #if WITH_LIB_SM |
| GICREG(0, GICC_CTLR) = 0xb; // enable GIC0 and select fiq mode for secure |
| GICREG(0, GICD_IGROUPR(0)) = ~0U; /* GICD_IGROUPR0 is banked */ |
| #else |
| GICREG(0, GICC_CTLR) = 1; // enable GIC0 |
| #endif |
| GICREG(0, GICC_PMR) = 0xFF; // unmask interrupts at all priority levels |
| } |
| |
| LK_INIT_HOOK_FLAGS(arm_gic_init_percpu, |
| arm_gic_init_percpu, |
| LK_INIT_LEVEL_PLATFORM_EARLY, LK_INIT_FLAG_SECONDARY_CPUS); |
| |
| static void arm_gic_suspend_cpu(uint level) |
| { |
| suspend_resume_fiq(false, false); |
| } |
| |
| LK_INIT_HOOK_FLAGS(arm_gic_suspend_cpu, arm_gic_suspend_cpu, |
| LK_INIT_LEVEL_PLATFORM, LK_INIT_FLAG_CPU_OFF); |
| |
| static void arm_gic_resume_cpu(uint level) |
| { |
| spin_lock_saved_state_t state; |
| bool resume_gicd = false; |
| |
| spin_lock_save(&gicd_lock, &state, GICD_LOCK_FLAGS); |
| if (!(GICREG(0, GICD_CTLR) & 1)) { |
| dprintf(SPEW, "%s: distibutor is off, calling arm_gic_init instead\n", __func__); |
| arm_gic_init(); |
| resume_gicd = true; |
| } else { |
| arm_gic_init_percpu(0); |
| } |
| spin_unlock_restore(&gicd_lock, state, GICD_LOCK_FLAGS); |
| suspend_resume_fiq(false, resume_gicd); |
| } |
| |
| LK_INIT_HOOK_FLAGS(arm_gic_resume_cpu, arm_gic_resume_cpu, |
| LK_INIT_LEVEL_PLATFORM, LK_INIT_FLAG_CPU_RESUME); |
| |
| static int arm_gic_max_cpu(void) |
| { |
| return (GICREG(0, GICD_TYPER) >> 5) & 0x7; |
| } |
| |
| void arm_gic_init(void) |
| { |
| int i; |
| |
| for (i = 0; i < MAX_INT; i+= 32) { |
| GICREG(0, GICD_ICENABLER(i / 32)) = ~0; |
| GICREG(0, GICD_ICPENDR(i / 32)) = ~0; |
| } |
| |
| if (arm_gic_max_cpu() > 0) { |
| /* Set external interrupts to target cpu 0 */ |
| for (i = 32; i < MAX_INT; i += 4) { |
| GICREG(0, GICD_ITARGETSR(i / 4)) = gicd_itargetsr[i / 4]; |
| } |
| } |
| |
| GICREG(0, GICD_CTLR) = 1; // enable GIC0 |
| #if WITH_LIB_SM |
| GICREG(0, GICD_CTLR) = 3; // enable GIC0 ns interrupts |
| /* |
| * Iterate through all IRQs and set them to non-secure |
| * mode. This will allow the non-secure side to handle |
| * all the interrupts we don't explicitly claim. |
| */ |
| for (i = 32; i < MAX_INT; i += 32) { |
| u_int reg = i / 32; |
| GICREG(0, GICD_IGROUPR(reg)) = gicd_igroupr[reg]; |
| } |
| #endif |
| arm_gic_init_percpu(0); |
| } |
| |
| static status_t arm_gic_set_secure_locked(u_int irq, bool secure) |
| { |
| #if WITH_LIB_SM |
| int reg = irq / 32; |
| uint32_t mask = 1ULL << (irq % 32); |
| |
| if (irq >= MAX_INT) |
| return ERR_INVALID_ARGS; |
| |
| if (secure) |
| GICREG(0, GICD_IGROUPR(reg)) = (gicd_igroupr[reg] &= ~mask); |
| else |
| GICREG(0, GICD_IGROUPR(reg)) = (gicd_igroupr[reg] |= mask); |
| LTRACEF("irq %d, secure %d, GICD_IGROUP%d = %x\n", |
| irq, secure, reg, GICREG(0, GICD_IGROUPR(reg))); |
| #endif |
| return NO_ERROR; |
| } |
| |
| static status_t arm_gic_set_target_locked(u_int irq, u_int cpu_mask, u_int enable_mask) |
| { |
| u_int reg = irq / 4; |
| u_int shift = 8 * (irq % 4); |
| u_int old_val; |
| u_int new_val; |
| |
| cpu_mask = (cpu_mask & 0xff) << shift; |
| enable_mask = (enable_mask << shift) & cpu_mask; |
| |
| old_val = GICREG(0, GICD_ITARGETSR(reg)); |
| new_val = (gicd_itargetsr[reg] & ~cpu_mask) | enable_mask; |
| GICREG(0, GICD_ITARGETSR(reg)) = gicd_itargetsr[reg] = new_val; |
| LTRACEF("irq %i, GICD_ITARGETSR%d %x => %x (got %x)\n", |
| irq, reg, old_val, new_val, GICREG(0, GICD_ITARGETSR(reg))); |
| |
| return NO_ERROR; |
| } |
| |
| static status_t arm_gic_get_priority(u_int irq) |
| { |
| u_int reg = irq / 4; |
| u_int shift = 8 * (irq % 4); |
| return (GICREG(0, GICD_IPRIORITYR(reg)) >> shift) & 0xff; |
| } |
| |
| static status_t arm_gic_set_priority_locked(u_int irq, uint8_t priority) |
| { |
| u_int reg = irq / 4; |
| u_int shift = 8 * (irq % 4); |
| u_int mask = 0xff << shift; |
| uint32_t regval; |
| |
| regval = GICREG(0, GICD_IPRIORITYR(reg)); |
| LTRACEF("irq %i, old GICD_IPRIORITYR%d = %x\n", irq, reg, regval); |
| regval = (regval & ~mask) | ((uint32_t)priority << shift); |
| GICREG(0, GICD_IPRIORITYR(reg)) = regval; |
| LTRACEF("irq %i, new GICD_IPRIORITYR%d = %x, req %x\n", |
| irq, reg, GICREG(0, GICD_IPRIORITYR(reg)), regval); |
| |
| return 0; |
| } |
| |
| status_t arm_gic_sgi(u_int irq, u_int flags, u_int cpu_mask) |
| { |
| u_int val = |
| ((flags & ARM_GIC_SGI_FLAG_TARGET_FILTER_MASK) << 24) | |
| ((cpu_mask & 0xff) << 16) | |
| ((flags & ARM_GIC_SGI_FLAG_NS) ? (1U << 15) : 0) | |
| (irq & 0xf); |
| |
| if (irq >= 16) |
| return ERR_INVALID_ARGS; |
| |
| LTRACEF("GICD_SGIR: %x\n", val); |
| |
| GICREG(0, GICD_SGIR) = val; |
| |
| return NO_ERROR; |
| } |
| |
| status_t mask_interrupt(unsigned int vector) |
| { |
| if (vector >= MAX_INT) |
| return ERR_INVALID_ARGS; |
| |
| if (arm_gic_interrupt_change_allowed(vector)) |
| gic_set_enable(vector, false); |
| |
| return NO_ERROR; |
| } |
| |
| status_t unmask_interrupt(unsigned int vector) |
| { |
| if (vector >= MAX_INT) |
| return ERR_INVALID_ARGS; |
| |
| if (arm_gic_interrupt_change_allowed(vector)) |
| gic_set_enable(vector, true); |
| |
| return NO_ERROR; |
| } |
| |
| static |
| enum handler_return __platform_irq(struct iframe *frame) |
| { |
| // get the current vector |
| uint32_t iar = GICREG(0, GICC_IAR); |
| unsigned int vector = iar & 0x3ff; |
| |
| if (vector >= 0x3fe) { |
| // spurious |
| return INT_NO_RESCHEDULE; |
| } |
| |
| THREAD_STATS_INC(interrupts); |
| KEVLOG_IRQ_ENTER(vector); |
| |
| uint cpu = arch_curr_cpu_num(); |
| |
| LTRACEF_LEVEL(2, "iar 0x%x cpu %u currthread %p vector %d pc 0x%lx\n", iar, cpu, |
| get_current_thread(), vector, (uintptr_t)IFRAME_PC(frame)); |
| |
| // deliver the interrupt |
| enum handler_return ret; |
| |
| ret = INT_NO_RESCHEDULE; |
| struct int_handler_struct *handler = get_int_handler(vector, cpu); |
| if (handler->handler) |
| ret = handler->handler(handler->arg); |
| |
| GICREG(0, GICC_EOIR) = iar; |
| |
| LTRACEF_LEVEL(2, "cpu %u exit %d\n", cpu, ret); |
| |
| KEVLOG_IRQ_EXIT(vector); |
| |
| return ret; |
| } |
| |
| enum handler_return platform_irq(struct iframe *frame) |
| { |
| #if WITH_LIB_SM |
| uint32_t ahppir = GICREG(0, GICC_AHPPIR); |
| uint32_t pending_irq = ahppir & 0x3ff; |
| struct int_handler_struct *h; |
| uint cpu = arch_curr_cpu_num(); |
| |
| LTRACEF("ahppir %d\n", ahppir); |
| if (pending_irq < MAX_INT && get_int_handler(pending_irq, cpu)->handler) { |
| enum handler_return ret = 0; |
| uint32_t irq; |
| uint8_t old_priority; |
| spin_lock_saved_state_t state; |
| |
| spin_lock_save(&gicd_lock, &state, GICD_LOCK_FLAGS); |
| |
| /* Temporarily raise the priority of the interrupt we want to |
| * handle so another interrupt does not take its place before |
| * we can acknowledge it. |
| */ |
| old_priority = arm_gic_get_priority(pending_irq); |
| arm_gic_set_priority_locked(pending_irq, 0); |
| DSB; |
| irq = GICREG(0, GICC_AIAR) & 0x3ff; |
| arm_gic_set_priority_locked(pending_irq, old_priority); |
| |
| spin_unlock_restore(&gicd_lock, state, GICD_LOCK_FLAGS); |
| |
| LTRACEF("irq %d\n", irq); |
| if (irq < MAX_INT && (h = get_int_handler(pending_irq, cpu))->handler) |
| ret = h->handler(h->arg); |
| else |
| TRACEF("unexpected irq %d != %d may get lost\n", irq, pending_irq); |
| GICREG(0, GICC_AEOIR) = irq; |
| return ret; |
| } |
| return sm_handle_irq(); |
| #else |
| return __platform_irq(frame); |
| #endif |
| } |
| |
| void platform_fiq(struct iframe *frame) |
| { |
| #if WITH_LIB_SM |
| sm_handle_fiq(); |
| #else |
| PANIC_UNIMPLEMENTED; |
| #endif |
| } |
| |
| #if WITH_LIB_SM |
| static status_t arm_gic_get_next_irq_locked(u_int min_irq, bool per_cpu) |
| { |
| u_int irq; |
| u_int max_irq = per_cpu ? GIC_MAX_PER_CPU_INT : MAX_INT; |
| uint cpu = arch_curr_cpu_num(); |
| |
| if (!per_cpu && min_irq < GIC_MAX_PER_CPU_INT) |
| min_irq = GIC_MAX_PER_CPU_INT; |
| |
| for (irq = min_irq; irq < max_irq; irq++) |
| if (get_int_handler(irq, cpu)->handler) |
| return irq; |
| |
| return SM_ERR_END_OF_INPUT; |
| } |
| |
| long smc_intc_get_next_irq(smc32_args_t *args) |
| { |
| status_t ret; |
| spin_lock_saved_state_t state; |
| |
| spin_lock_save(&gicd_lock, &state, GICD_LOCK_FLAGS); |
| |
| arm_gic_non_secure_interrupts_frozen = true; |
| ret = arm_gic_get_next_irq_locked(args->params[0], args->params[1]); |
| LTRACEF("min_irq %d, per_cpu %d, ret %d\n", |
| args->params[0], args->params[1], ret); |
| |
| spin_unlock_restore(&gicd_lock, state, GICD_LOCK_FLAGS); |
| |
| return ret; |
| } |
| |
| static u_long enabled_fiq_mask[BITMAP_NUM_WORDS(MAX_INT)]; |
| |
| static void bitmap_update_locked(u_long *bitmap, u_int bit, bool set) |
| { |
| u_long mask = 1UL << BITMAP_BIT_IN_WORD(bit); |
| |
| bitmap += BITMAP_WORD(bit); |
| if (set) |
| *bitmap |= mask; |
| else |
| *bitmap &= ~mask; |
| } |
| |
| long smc_intc_request_fiq(smc32_args_t *args) |
| { |
| u_int fiq = args->params[0]; |
| bool enable = args->params[1]; |
| spin_lock_saved_state_t state; |
| |
| dprintf(SPEW, "%s: fiq %d, enable %d\n", __func__, fiq, enable); |
| spin_lock_save(&gicd_lock, &state, GICD_LOCK_FLAGS); |
| |
| arm_gic_set_secure_locked(fiq, true); |
| arm_gic_set_target_locked(fiq, ~0, ~0); |
| arm_gic_set_priority_locked(fiq, 0); |
| |
| gic_set_enable(fiq, enable); |
| bitmap_update_locked(enabled_fiq_mask, fiq, enable); |
| |
| dprintf(SPEW, "%s: fiq %d, enable %d done\n", __func__, fiq, enable); |
| |
| spin_unlock_restore(&gicd_lock, state, GICD_LOCK_FLAGS); |
| |
| return NO_ERROR; |
| } |
| |
| long smc_intc_fiq_resume(smc32_args_t *args) |
| { |
| suspend_resume_fiq(true, false); |
| |
| return 0; |
| } |
| |
| static u_int current_fiq[8] = { 0x3ff, 0x3ff, 0x3ff, 0x3ff, 0x3ff, 0x3ff, 0x3ff, 0x3ff }; |
| |
| static bool update_fiq_targets(u_int cpu, bool enable, u_int triggered_fiq, bool resume_gicd) |
| { |
| u_int i, j; |
| u_long mask; |
| u_int fiq; |
| bool smp = arm_gic_max_cpu() > 0; |
| bool ret = false; |
| |
| spin_lock(&gicd_lock); /* IRQs and FIQs are already masked */ |
| for (i = 0; i < BITMAP_NUM_WORDS(MAX_INT); i++) { |
| mask = enabled_fiq_mask[i]; |
| while (mask) { |
| j = _ffz(~mask); |
| mask &= ~(1UL << j); |
| fiq = i * BITMAP_BITS_PER_WORD + j; |
| if (fiq == triggered_fiq) |
| ret = true; |
| LTRACEF("cpu %d, irq %i, enable %d\n", cpu, fiq, enable); |
| if (smp) |
| arm_gic_set_target_locked(fiq, 1U << cpu, enable ? ~0 : 0); |
| if (!smp || resume_gicd) |
| gic_set_enable(fiq, enable || smp); |
| } |
| } |
| spin_unlock(&gicd_lock); |
| return ret; |
| } |
| |
| static void suspend_resume_fiq(bool resume_gicc, bool resume_gicd) |
| { |
| u_int cpu = arch_curr_cpu_num(); |
| |
| ASSERT(cpu < 8); |
| |
| update_fiq_targets(cpu, resume_gicc, ~0, resume_gicd); |
| } |
| |
| status_t sm_intc_fiq_enter(void) |
| { |
| u_int cpu = arch_curr_cpu_num(); |
| u_int irq = GICREG(0, GICC_IAR) & 0x3ff; |
| bool fiq_enabled; |
| |
| ASSERT(cpu < 8); |
| |
| LTRACEF("cpu %d, irq %i\n", cpu, irq); |
| |
| if (irq >= 1020) { |
| LTRACEF("spurious fiq: cpu %d, old %d, new %d\n", cpu, current_fiq[cpu], irq); |
| return ERR_NO_MSG; |
| } |
| |
| fiq_enabled = update_fiq_targets(cpu, false, irq, false); |
| GICREG(0, GICC_EOIR) = irq; |
| |
| if (current_fiq[cpu] != 0x3ff) { |
| dprintf(INFO, "more than one fiq active: cpu %d, old %d, new %d\n", cpu, current_fiq[cpu], irq); |
| return ERR_ALREADY_STARTED; |
| } |
| |
| if (!fiq_enabled) { |
| dprintf(INFO, "got disabled fiq: cpu %d, new %d\n", cpu, irq); |
| return ERR_NOT_READY; |
| } |
| |
| current_fiq[cpu] = irq; |
| |
| return 0; |
| } |
| |
| void sm_intc_fiq_exit(void) |
| { |
| u_int cpu = arch_curr_cpu_num(); |
| |
| ASSERT(cpu < 8); |
| |
| LTRACEF("cpu %d, irq %i\n", cpu, current_fiq[cpu]); |
| if (current_fiq[cpu] == 0x3ff) { |
| dprintf(INFO, "%s: no fiq active, cpu %d\n", __func__, cpu); |
| return; |
| } |
| update_fiq_targets(cpu, true, current_fiq[cpu], false); |
| current_fiq[cpu] = 0x3ff; |
| } |
| #endif |