#pragma once #include #include #include #include #include #define VIM_NUM_INTERRUPTS 256 struct vim_group { uint32_t raw; uint32_t sts; uint32_t intr_en_set; uint32_t intr_en_clr; uint32_t irqsts; uint32_t fiqsts; uint32_t intmap; uint32_t inttype; }; struct vim { uint32_t pid; uint32_t info; uint32_t priirq; uint32_t prifiq; uint32_t irqgsts; uint32_t fiqgsts; void (*irqvec)(void); void (*fiqvec)(void); uint32_t actirq; uint32_t actfiq; uint32_t irqprimsk; uint32_t fiqprimsk; void (*dedvec)(void); uint32_t padding0[243]; struct vim_group groups[8]; uint32_t padding1[704]; uint32_t priority[VIM_NUM_INTERRUPTS]; uint32_t padding2[768]; void (*vector[VIM_NUM_INTERRUPTS])(void); }; #define VIM_BASE 0x50F00000UL static volatile struct vim *const VIM = (volatile struct vim *)VIM_BASE; #define VIM_BIT_SET(field, i) \ do \ { \ VIM->groups[(i) / 32].field = UINT32_C(1) << ((i) % 32); \ } while (0) static inline void vim_enable(uint32_t i) { VIM_BIT_SET(intr_en_set, i); } static inline void vim_disable(uint32_t i) { VIM_BIT_SET(intr_en_clr, i); } static inline void vim_set(uint32_t i) { VIM_BIT_SET(raw, i); } static inline void vim_clear(uint32_t i) { VIM_BIT_SET(sts, i); } static inline void vim_set_is_fiq(uint32_t i) { VIM->groups[(i) / 32].intmap |= UINT32_C(1) << ((i) % 32); } static inline void vim_clear_is_fiq(uint32_t i) { VIM->groups[(i) / 32].intmap &= ~(UINT32_C(1) << ((i) % 32)); } static inline void vim_finish_irq(void) { // Store an arbitrary value into irqvec. __asm__("str r0, [%0, %1]" : : "r"(VIM), "i"(offsetof(struct vim, irqvec))); } static inline void vim_finish_fiq(void) { // Store an arbitrary value into fiqvec. __asm__("str r0, [%0, %1]" : : "r"(VIM), "i"(offsetof(struct vim, fiqvec))); } static inline void vim_set_priority(uint32_t i, uint8_t priority) { VIM->priority[i] = priority; } static inline void vim_set_handler(uint32_t i, void (*handler)(void)) { VIM->vector[i] = handler; } static inline void vim_set_ded_handler(void (*handler)(void)) { VIM->dedvec = handler; } /* * VIM_IRQ_HANDLER_NESTING_{NOFP,FP} generate wrapper functions to turn any * function into an interrupt handler that allows other interrupts to occur * while the handler runs. The FP version must be used if the function uses * floating-point; the handler will save the volatile floating-point context * and restore it after the handler body is complete. If a handler uses * floating-point and doesn't use the FP version of the macro, then an * undefined instruction exception will occur if the interrupt triggers while * the idle task is running, because the idle task doesn't enable the * floating-point unit in FPEXC. * NOTE: Some implementations of standard library functions like memcpy and * memset can use floating-point internally as an optimization. * * The handler body is responsible for clearing the interrupt source and * clearing the interrupt in the VIM using vim_clear, but it should not call * vim_finish_irq. This is done by the generated handler function because it * must occur while interrupts are re-disabled in the epilogue of the handler. * * For non-nesting interrupts, use the RT_IRQ_HANDLER_{NOFP,FP} macros from * rt/arch/irq.h, which are generic to any Cortex-A/R. Handlers that use those * macros must call vim_finish_irq in the handler body as the last step, after * the interrupt has been cleared. * * Supporting nesting for FIQs is technically possible, but it would not be * very useful because the additional banked FIQ registers (r8-r12) can't be * used, as the handler must switch to SVC mode in order to re-enable FIQs, and * this should be done as quickly as possible in order to minimize latency. The * Sitara VIM allows any interrupt to be made into an IRQ or an FIQ, so for * interrupts that should support nesting, these can just be made into IRQs * with the appropriate priority. For FIQ handlers, use * RT_FIQ_HANDLER_{NOFP,FP}. */ #define VIM_IRQ_HANDLER_NESTING_NOFP(fn) \ __attribute__((naked, target("general-regs-only"), \ target(RT_IRQ_DEFAULT_ISA), aligned(4))) void \ fn(void) \ { \ __asm__("sub lr, #4;" \ "srsdb sp!, %[mode_svc];" \ "cpsie i, %[mode_svc];" \ "push {r0-r3, r12, lr};" \ "bl " #fn "_body;" \ "pop {r0-r3};" \ "movw r12, %[vim_lo];" \ "movt r12, %[vim_hi];" \ "cpsid i;" \ "str r0, [r12, %[irqvec_offset]];" \ "pop {r12, lr};" \ "rfeia sp!;" \ : \ : [mode_svc] "i"(MODE_SVC), [vim_lo] "i"(VIM_BASE & 0xFFFFU), \ [vim_hi] "i"(VIM_BASE >> 16), \ [irqvec_offset] "i"(offsetof(struct vim, irqvec))); \ } \ __attribute__((noinline, target("general-regs-only"), \ used)) static void fn##_body(void) #if RT_ARM_FP #define VIM_IRQ_HANDLER_NESTING_FP(fn) \ __attribute__((naked, target(RT_IRQ_DEFAULT_ISA), aligned(4))) void \ fn(void) \ { \ __asm__("sub lr, #4;" \ "srsdb sp!, %[mode_svc];" \ "cpsie i, %[mode_svc];" \ "push {r0-r5, r12, lr};" \ "vmrs r4, fpexc;" \ "movs r0, %[fpexc_en];" \ "vmsr fpexc, r0;" \ "tst r4, r0;" \ "beq 0f;" \ "vpush {d0-d7};" \ "vmrs r5, fpscr;" \ "0: movs r0, #0;" \ "vmsr fpscr, r0;" \ "bl " #fn "_body;" \ "tst r4, %[fpexc_en];" \ "beq 0f;" \ "vpop {d0-d7};" \ "vmsr fpscr, r5;" \ "0: vmsr fpexc, r4;" \ "pop {r0-r5};" \ "movw r12, %[vim_lo];" \ "movt r12, %[vim_hi];" \ "cpsid i;" \ "str r0, [r12, %[irqvec_offset]];" \ "pop {r12, lr};" \ "rfeia sp!;" \ : \ : [mode_svc] "i"(MODE_SVC), [fpexc_en] "i"(RT_FPEXC_EN), \ [vim_lo] "i"(VIM_BASE & 0xFFFFU), \ [vim_hi] "i"(VIM_BASE >> 16), \ [irqvec_offset] "i"(offsetof(struct vim, irqvec))); \ } \ __attribute__((noinline, used)) static void fn##_body(void) #else // !RT_ARM_FP #define VIM_IRQ_HANDLER_NESTING_FP VIM_IRQ_HANDLER_NESTING_NOFP #endif // RT_ARM_FP