/* * Copyright (c) 2023 Meta * * SPDX-License-Identifier: Apache-2.0 */ /** * @file * @brief Public interface for multi-level interrupts */ #ifndef ZEPHYR_INCLUDE_IRQ_MULTILEVEL_H_ #define ZEPHYR_INCLUDE_IRQ_MULTILEVEL_H_ #ifndef _ASMLANGUAGE #include #include #include #ifdef __cplusplus extern "C" { #endif #if defined(CONFIG_MULTI_LEVEL_INTERRUPTS) || defined(__DOXYGEN__) typedef union _z_irq { /* Zephyr multilevel-encoded IRQ */ uint32_t irq; /* Interrupt bits */ struct { /* First level interrupt bits */ uint32_t l1: CONFIG_1ST_LEVEL_INTERRUPT_BITS; /* Second level interrupt bits */ uint32_t l2: CONFIG_2ND_LEVEL_INTERRUPT_BITS; /* Third level interrupt bits */ uint32_t l3: CONFIG_3RD_LEVEL_INTERRUPT_BITS; } bits; /* Third level IRQ's interrupt controller */ struct { /* IRQ of the third level interrupt aggregator */ uint32_t irq: CONFIG_1ST_LEVEL_INTERRUPT_BITS + CONFIG_2ND_LEVEL_INTERRUPT_BITS; } l3_intc; /* Second level IRQ's interrupt controller */ struct { /* IRQ of the second level interrupt aggregator */ uint32_t irq: CONFIG_1ST_LEVEL_INTERRUPT_BITS; } l2_intc; } _z_irq_t; BUILD_ASSERT(sizeof(_z_irq_t) == sizeof(uint32_t), "Size of `_z_irq_t` must equal to `uint32_t`"); static inline uint32_t _z_l1_irq(_z_irq_t irq) { return irq.bits.l1; } static inline uint32_t _z_l2_irq(_z_irq_t irq) { return irq.bits.l2 - 1; } static inline uint32_t _z_l3_irq(_z_irq_t irq) { return irq.bits.l3 - 1; } static inline unsigned int _z_irq_get_level(_z_irq_t z_irq) { if (z_irq.bits.l3 != 0) { return 3; } if (z_irq.bits.l2 != 0) { return 2; } return 1; } /** * @brief Return IRQ level * This routine returns the interrupt level number of the provided interrupt. * * @param irq IRQ number in its zephyr format * * @return 1 if IRQ level 1, 2 if IRQ level 2, 3 if IRQ level 3 */ static inline unsigned int irq_get_level(unsigned int irq) { _z_irq_t z_irq = { .irq = irq, }; return _z_irq_get_level(z_irq); } /** * @brief Return the 2nd level interrupt number * * This routine returns the second level irq number of the zephyr irq * number passed in * * @param irq IRQ number in its zephyr format * * @return 2nd level IRQ number */ static inline unsigned int irq_from_level_2(unsigned int irq) { _z_irq_t z_irq = { .irq = irq, }; return _z_l2_irq(z_irq); } /** * @brief Preprocessor macro to convert `irq` from level 1 to level 2 format * * @param irq IRQ number in its zephyr format * * @return 2nd level IRQ number */ #define IRQ_TO_L2(irq) ((irq + 1) << CONFIG_1ST_LEVEL_INTERRUPT_BITS) /** * @brief Converts irq from level 1 to level 2 format * * * This routine converts the input into the level 2 irq number format * * @note Values >= 0xFF are invalid * * @param irq IRQ number in its zephyr format * * @return 2nd level IRQ number */ static inline unsigned int irq_to_level_2(unsigned int irq) { _z_irq_t z_irq = { .bits = { .l1 = 0, .l2 = irq + 1, .l3 = 0, }, }; return z_irq.irq; } /** * @brief Returns the parent IRQ of the level 2 raw IRQ number * * * The parent of a 2nd level interrupt is in the 1st byte * * @param irq IRQ number in its zephyr format * * @return 2nd level IRQ parent */ static inline unsigned int irq_parent_level_2(unsigned int irq) { _z_irq_t z_irq = { .irq = irq, }; return _z_l1_irq(z_irq); } /** * @brief Return the 3rd level interrupt number * * * This routine returns the third level irq number of the zephyr irq * number passed in * * @param irq IRQ number in its zephyr format * * @return 3rd level IRQ number */ static inline unsigned int irq_from_level_3(unsigned int irq) { _z_irq_t z_irq = { .irq = irq, }; return _z_l3_irq(z_irq); } /** * @brief Preprocessor macro to convert `irq` from level 1 to level 3 format * * @param irq IRQ number in its zephyr format * * @return 3rd level IRQ number */ #define IRQ_TO_L3(irq) \ ((irq + 1) << (CONFIG_1ST_LEVEL_INTERRUPT_BITS + CONFIG_2ND_LEVEL_INTERRUPT_BITS)) /** * @brief Converts irq from level 1 to level 3 format * * * This routine converts the input into the level 3 irq number format * * @note Values >= 0xFF are invalid * * @param irq IRQ number in its zephyr format * * @return 3rd level IRQ number */ static inline unsigned int irq_to_level_3(unsigned int irq) { _z_irq_t z_irq = { .bits = { .l1 = 0, .l2 = 0, .l3 = irq + 1, }, }; return z_irq.irq; } /** * @brief Returns the parent IRQ of the level 3 raw IRQ number * * * The parent of a 3rd level interrupt is in the 2nd byte * * @param irq IRQ number in its zephyr format * * @return 3rd level IRQ parent */ static inline unsigned int irq_parent_level_3(unsigned int irq) { _z_irq_t z_irq = { .irq = irq, }; return _z_l2_irq(z_irq); } /** * @brief Return the interrupt number for a given level * * @param irq IRQ number in its zephyr format * @param level IRQ level * * @return IRQ number in the level */ static inline unsigned int irq_from_level(unsigned int irq, unsigned int level) { if (level == 1) { return irq; } else if (level == 2) { return irq_from_level_2(irq); } else if (level == 3) { return irq_from_level_3(irq); } /* level is higher than 3 */ __ASSERT_NO_MSG(false); return irq; } /** * @brief Converts irq from level 1 to a given level * * @param irq IRQ number in its zephyr format * @param level IRQ level * * @return Converted IRQ number in the level */ static inline unsigned int irq_to_level(unsigned int irq, unsigned int level) { if (level == 1) { return irq; } else if (level == 2) { return irq_to_level_2(irq); } else if (level == 3) { return irq_to_level_3(irq); } /* level is higher than 3 */ __ASSERT_NO_MSG(false); return irq; } /** * @brief Returns the parent IRQ of the given level raw IRQ number * * @param irq IRQ number in its zephyr format * @param level IRQ level * * @return IRQ parent of the given level */ static inline unsigned int irq_parent_level(unsigned int irq, unsigned int level) { if (level == 1) { /* doesn't really make sense, but return anyway */ return irq; } else if (level == 2) { return irq_parent_level_2(irq); } else if (level == 3) { return irq_parent_level_3(irq); } /* level is higher than 3 */ __ASSERT_NO_MSG(false); return irq; } /** * @brief Returns the parent interrupt controller IRQ of the given IRQ number * * @param irq IRQ number in its zephyr format * * @return IRQ of the interrupt controller */ static inline unsigned int irq_get_intc_irq(unsigned int irq) { const unsigned int level = irq_get_level(irq); __ASSERT_NO_MSG(level <= 3); _z_irq_t z_irq = { .irq = irq, }; if (level == 3) { return z_irq.l3_intc.irq; } else if (level == 2) { return z_irq.l2_intc.irq; } else { return irq; } } /** * @brief Increments the multilevel-encoded @a irq by @a val * * @param irq IRQ number in its zephyr format * @param val Amount to increment * * @return @a irq incremented by @a val */ static inline unsigned int irq_increment(unsigned int irq, unsigned int val) { _z_irq_t z_irq = { .irq = irq, }; if (z_irq.bits.l3 != 0) { z_irq.bits.l3 += val; } else if (z_irq.bits.l2 != 0) { z_irq.bits.l2 += val; } else { z_irq.bits.l1 += val; } return z_irq.irq; } #endif /* CONFIG_MULTI_LEVEL_INTERRUPTS */ #ifdef __cplusplus } #endif #endif /* _ASMLANGUAGE */ #endif /* ZEPHYR_INCLUDE_IRQ_MULTILEVEL_H_ */