#pragma once @ vim:ft=arm /* This file provides the entry and exit sequences for the syscall handler * specific to the Interrupt Controller (INTC) as implemented on TI Sitara * AM335x processors. */ #include "intc.h" #include "vic.h" #include #include #define SW_IRQ_BITMASK (1UL << (RT_INTC_SW_IRQ % 32)) #define SW_IRQ_CLEAR_OFFSET INTC_ISR_CLEAR_OFFSET(RT_INTC_SW_IRQ / 32) .macro vic_svc_start mov32 r12, INTC_BASE mov lr, INTC_PRIORITY_LOWEST str lr, [r12, INTC_THRESHOLD_OFFSET] /* Setting the NEWIRQAGR bit and a dsb are ordinarily needed when adjusting * the priority threshold to cause the INTC to re-evaluate pending * interrupts. In this case though we are masking the SW_IRQ and it is only * triggered by a write to INTC during another IRQ. IRQs are globally * masked at this point, so the SW_IRQ cannot be pending here, so * re-sorting IRQs is not needed. If a different IRQ of the lowest priority * occurs immediately after this regardless, it is not a problem, because * only the SW_IRQ must be blocked. */ .endm .macro disable_priority_threshold mov32 r1, INTC_BASE movs r0, INTC_THRESHOLD_DISABLED str r0, [r1, INTC_THRESHOLD_OFFSET] movs r0, INTC_CONTROL_NEWIRQAGR str r0, [r1, INTC_CONTROL_OFFSET] /* dsb is not needed. Newly-unmasked interrupts will still occur once IRQs * are re-enabled. */ .endm .macro vic_svc_finish disable_priority_threshold .endm .macro vic_syscall_irq_start mov32 r1, INTC_BASE movs r0, SW_IRQ_BITMASK str r0, [r1, SW_IRQ_CLEAR_OFFSET] movs r0, INTC_PRIORITY_LOWEST str r0, [r1, INTC_THRESHOLD_OFFSET] movs r0, INTC_CONTROL_NEWIRQAGR str r0, [r1, INTC_CONTROL_OFFSET] /* A dsb is needed here because the syscall IRQ was pending prior to these * writes, and following this sequence IRQs will be re-enabled, so the * syscall IRQ must be cleared and masked before then. */ dsb .endm .macro vic_syscall_irq_finish disable_priority_threshold .endm // irq_handler should be placed directly into the exception vector if FIQs aren't used. .macro irq_handler /* In order to simulate hardware-vectored interrupts, we use 3 words of the * IRQ stack so we can use r0 and r1 registers for interacting with the * INTC. Once the IRQ handler address is known, we place it on the stack as * well, and do a pop of the original values of r0 and r1 and the IRQ * handler address into pc. This handler will then need to observe the same * rules of being a direct IRQ handler (i.e., it must preserve volatile * state and do an exception return from IRQ mode). */ // |r0|r1|__| // |<-------sp strd r0, r1, [sp, -12]! mov32 r0, INTC_BASE mov32 r1, intc_vector ldr r0, [r0, INTC_SIR_IRQ_OFFSET] and r0, INTC_SIR_ACTIVE_MASK ldr r1, [r1, r0, lsl 2] str r1, [sp, 8] // |r0|r1|pc| // sp------>| pop {r0, r1, pc} .endm .macro fiq_handler mov32 r8, INTC_BASE mov32 r9, intc_vector ldr r8, [r8, INTC_SIR_FIQ_OFFSET] and r8, INTC_SIR_ACTIVE_MASK ldr pc, [r9, r8, lsl 2] .endm