#pragma once #define INTC_BASE 0x48200000U #define INTC_NUM_INTERRUPTS 128U #define INTC_SIR_IRQ_OFFSET 0x40U #define INTC_SIR_FIQ_OFFSET 0x44U #define INTC_SIR_ACTIVE_MASK 0x7FU #define INTC_CONTROL_OFFSET 0x48U #define INTC_CONTROL_NEWIRQAGR 0x1U #define INTC_CONTROL_NEWFIQAGR 0x2U #define INTC_IRQ_PRIORITY_OFFSET 0x60U #define INTC_FIQ_PRIORITY_OFFSET 0x64U #define INTC_THRESHOLD_OFFSET 0x68U #define INTC_PRIORITY_LOWEST 0x3FU // Priority zero can't be masked with the threshold. #define INTC_PRIORITY_HIGHEST 0x1U #define INTC_THRESHOLD_DISABLED 0xFFU #define INTC_ISR_CLEAR_OFFSET(g) (0x94U + (g) * 32U) #if !defined(__ASSEMBLER__) #include #include #include #include #include struct intc_group { uint32_t itr; uint32_t mir; uint32_t mir_clear; uint32_t mir_set; uint32_t isr_set; uint32_t isr_clear; uint32_t pending_irq; uint32_t pending_fiq; }; struct intc { uint32_t revision; uint32_t padding0[3]; uint32_t sysconfig; uint32_t sysstatus; uint32_t padding1[10]; uint32_t sir_irq; uint32_t sir_fiq; uint32_t control; uint32_t protection; uint32_t idle; uint32_t padding2[3]; uint32_t irq_priority; uint32_t fiq_priority; uint32_t threshold; uint32_t padding3[5]; struct intc_group group[4]; uint32_t ilr[INTC_NUM_INTERRUPTS]; }; static volatile struct intc *const INTC = (volatile struct intc *)INTC_BASE; rt_static_assert(offsetof(struct intc, control) == INTC_CONTROL_OFFSET, "the control field offset in intc is incorrect"); rt_static_assert(offsetof(struct intc, threshold) == INTC_THRESHOLD_OFFSET, "the threshold field offset in intc is incorrect"); rt_static_assert((offsetof(struct intc, group) + offsetof(struct intc_group, isr_clear)) == INTC_ISR_CLEAR_OFFSET(0), "the isr_clear field offset in intc is incorrect"); extern void (*volatile intc_vector[INTC_NUM_INTERRUPTS])(void); #define INTC_SYSCONFIG_AUTOIDLE (UINT32_C(1) << 0) #define INTC_SYSCONFIG_SOFTRESET (UINT32_C(1) << 1) #define INTC_SYSSTATUS_RESETDONE (UINT32_C(1) << 0) #define INTC_PROTECTION_ENABLE (UINT32_C(1) << 0) #define INTC_IDLE_FUNCIDLE (UINT32_C(1) << 0) #define INTC_IDLE_TURBO (UINT32_C(1) << 1) #define INTC_ILR_PRIORITY(priority) ((uint32_t)(priority) << 2) #define INTC_ILR_PRIORITY_MASK INTC_ILR_PRIORITY(INTC_PRIORITY_LOWEST) #define INTC_ILR_FIQNIRQ (UINT32_C(1) << 0) static inline void intc_reset(void) { INTC->sysconfig = INTC_SYSCONFIG_SOFTRESET; while ((INTC->sysstatus & INTC_SYSSTATUS_RESETDONE) == 0) { // Wait for INTC reset to complete. } } static inline void intc_protect(void) { INTC->protection = INTC_PROTECTION_ENABLE; } #define INTC_GROUP_REG_SET(reg, i) \ do \ { \ INTC->group[(i) / 32].reg = UINT32_C(1) << ((i) % 32); \ } while (0) static inline void intc_enable(uint32_t i) { INTC_GROUP_REG_SET(mir_clear, i); } static inline void intc_disable(uint32_t i) { INTC_GROUP_REG_SET(mir_set, i); } static inline void intc_set(uint32_t i) { INTC_GROUP_REG_SET(isr_set, i); } static inline void intc_clear(uint32_t i) { INTC_GROUP_REG_SET(isr_clear, i); } static inline void intc_set_priority(uint32_t i, uint8_t priority) { const uint32_t ilr = INTC->ilr[i] & ~INTC_ILR_PRIORITY_MASK; INTC->ilr[i] = ilr | INTC_ILR_PRIORITY(priority); } enum intc_type { INTC_TYPE_IRQ, INTC_TYPE_FIQ, }; static inline void intc_set_type(uint32_t i, enum intc_type type) { if (type == INTC_TYPE_FIQ) { INTC->ilr[i] |= INTC_ILR_FIQNIRQ; } else { INTC->ilr[i] &= ~INTC_ILR_FIQNIRQ; } } static inline void intc_finish_irq(void) { INTC->control = INTC_CONTROL_NEWIRQAGR; __asm__("dsb" ::: "memory"); } static inline void intc_finish_fiq(void) { INTC->control = INTC_CONTROL_NEWIRQAGR | INTC_CONTROL_NEWFIQAGR; __asm__("dsb" ::: "memory"); } #define INTC_IRQ_HANDLER_NESTING_NOFP(fn) \ __attribute__((naked, target("general-regs-only"))) void fn(void) \ { \ __asm__("sub lr, #4;" \ "srsdb sp!, %[mode_svc];" \ "cps %[mode_svc];" \ "push {r0, r4-r6};" \ "movw r4, %[intc_lo];" \ "movt r4, %[intc_hi];" \ "ldr r5, [r4, %[threshold_offset]];" \ "ldr r0, [r4, %[irq_priority_offset]];" \ "str r0, [r4, %[threshold_offset]];" \ "movs r6, %[control_newirqagr];" \ "str r6, [r4, %[control_offset]];" \ "dsb;" \ "cpsie i;" \ "push {r0-r3, r12, lr};" \ "bl " #fn "_body;" \ "pop {r0-r3, r12, lr};" \ "cpsid i;" \ "str r5, [r4, %[threshold_offset]];" \ "str r6, [r4, %[control_offset]];" \ "dsb;" \ "pop {r0, r4-r6};" \ "rfeia sp!;" \ : \ : \ [mode_svc] "i"(MODE_SVC), [intc_lo] "i"(INTC_BASE & 0xFFFFU), \ [intc_hi] "i"(INTC_BASE >> 16), \ [threshold_offset] "i"(INTC_THRESHOLD_OFFSET), \ [irq_priority_offset] "i"(INTC_IRQ_PRIORITY_OFFSET), \ [control_newirqagr] "i"(INTC_CONTROL_NEWIRQAGR), \ [control_offset] "i"(INTC_CONTROL_OFFSET)); \ } \ __attribute__((noinline, target("general-regs-only"), \ used)) static void fn##_body(void) #if RT_ARM_FP #define INTC_IRQ_HANDLER_NESTING_FP(fn) \ __attribute__((naked)) void fn(void) \ { \ __asm__("sub lr, #4;" \ "srsdb sp!, %[mode_svc];" \ "cps %[mode_svc];" \ "push {r0, r4-r6};" \ "movw r4, %[intc_lo];" \ "movt r4, %[intc_hi];" \ "ldr r5, [r4, %[threshold_offset]];" \ "ldr r0, [r4, %[irq_priority_offset]];" \ "str r0, [r4, %[threshold_offset]];" \ "movs r6, %[control_newirqagr];" \ "str r6, [r4, %[control_offset]];" \ "dsb;" \ "cpsie i;" \ "push {r1-r3, r7-r9, r12, lr};" \ "vmrs r7, fpexc;" \ "movs r0, %[fpexc_en];" \ "vmsr fpexc, r0;" \ "tst r7, %[fpexc_en];" \ "beq 0f;" \ "vpush {d16-d31};" \ "vpush {d0-d7};" \ "vmrs r8, fpscr;" \ "0: movs r0, #0;" \ "vmsr fpscr, r0;" \ "bl " #fn "_body;" \ "tst r7, %[fpexc_en];" \ "beq 0f;" \ "vpop {d0-d7};" \ "vpop {d16-d31};" \ "vmsr fpscr, r8;" \ "0: vmsr fpexc, r7;" \ "pop {r1-r3, r7-r9, r12, lr};" \ "cpsid i;" \ "str r5, [r4, %[threshold_offset]];" \ "str r6, [r4, %[control_offset]];" \ "dsb;" \ "pop {r0, r4-r6};" \ "rfeia sp!;" \ : \ : [mode_svc] "i"(MODE_SVC), [fpexc_en] "i"(RT_FPEXC_EN), \ [intc_lo] "i"(INTC_BASE & 0xFFFFU), \ [intc_hi] "i"(INTC_BASE >> 16), \ [threshold_offset] "i"(INTC_THRESHOLD_OFFSET), \ [irq_priority_offset] "i"(INTC_IRQ_PRIORITY_OFFSET), \ [control_newirqagr] "i"(INTC_CONTROL_NEWIRQAGR), \ [control_offset] "i"(INTC_CONTROL_OFFSET)); \ } \ __attribute__((noinline, used)) static void fn##_body(void) #else // !RT_ARM_FP #define INTC_IRQ_HANDLER_NESTING_FP INTC_IRQ_HANDLER_NESTING_NOFP #endif // RT_ARM_FP #endif // !defined(__ASSEMBLER__)