/* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved. * * The information contained herein is property of Nordic Semiconductor ASA. * Terms and conditions of usage are described in detail in NORDIC * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. * * Licensees are granted free, non-transferable use of the information. NO * WARRANTY of ANY KIND is provided. This heading must NOT be removed from * the file. * */ #include "nrf_drv_common.h" #include "nrf_error.h" #include "nrf_assert.h" #include #include #include #include "nrf_drv_swi.h" #include "app_util_platform.h" STATIC_ASSERT(SWI_COUNT > 0); STATIC_ASSERT(SWI_COUNT <= SWI_MAX); STATIC_ASSERT(SWI_MAX_FLAGS <= sizeof(nrf_swi_flags_t) * 8); #ifdef SWI_DISABLE0 #undef SWI_DISABLE0 #define SWI_DISABLE0 1uL #else #if SWI_COUNT > 0 #define SWI_DISABLE0 0uL #else #define SWI_DISABLE0 1uL #endif #endif #ifdef SWI_DISABLE1 #undef SWI_DISABLE1 #define SWI_DISABLE1 1uL #else #if SWI_COUNT > 1 #define SWI_DISABLE1 0uL #else #define SWI_DISABLE1 1uL #endif #endif #ifdef SWI_DISABLE2 #undef SWI_DISABLE2 #define SWI_DISABLE2 1uL #else #if SWI_COUNT > 2 #define SWI_DISABLE2 0uL #else #define SWI_DISABLE2 1uL #endif #endif #ifdef SWI_DISABLE3 #undef SWI_DISABLE3 #define SWI_DISABLE3 1uL #else #if SWI_COUNT > 3 #define SWI_DISABLE3 0uL #else #define SWI_DISABLE3 1uL #endif #endif #ifdef SWI_DISABLE4 #undef SWI_DISABLE4 #define SWI_DISABLE4 1uL #else #if SWI_COUNT > 4 #define SWI_DISABLE4 0uL #else #define SWI_DISABLE4 1uL #endif #endif #ifdef SWI_DISABLE5 #undef SWI_DISABLE5 #define SWI_DISABLE5 1uL #else #if SWI_COUNT > 5 #define SWI_DISABLE5 0uL #else #define SWI_DISABLE5 1uL #endif #endif #define SWI_START_NUMBER ( (SWI_DISABLE0) \ + (SWI_DISABLE0 * SWI_DISABLE1) \ + (SWI_DISABLE0 * SWI_DISABLE1 * SWI_DISABLE2) \ + (SWI_DISABLE0 * SWI_DISABLE1 * SWI_DISABLE2 * SWI_DISABLE3) \ + (SWI_DISABLE0 * SWI_DISABLE1 * SWI_DISABLE2 * SWI_DISABLE3 * SWI_DISABLE4) \ + (SWI_DISABLE0 * SWI_DISABLE1 * SWI_DISABLE2 * SWI_DISABLE3 * SWI_DISABLE4 \ * SWI_DISABLE5) ) #define SWI_ARRAY_SIZE (SWI_COUNT - SWI_START_NUMBER) #if (SWI_COUNT <= SWI_START_NUMBER) #undef SWI_ARRAY_SIZE #define SWI_ARRAY_SIZE 1 #endif static nrf_drv_state_t m_drv_state = NRF_DRV_STATE_UNINITIALIZED; static nrf_swi_handler_t m_swi_handlers[SWI_ARRAY_SIZE]; #if !EGU_ENABLED static nrf_swi_flags_t m_swi_flags[SWI_ARRAY_SIZE]; #endif #if EGU_ENABLED > 0 /**@brief Get the specific EGU instance. */ __STATIC_INLINE NRF_EGU_Type * egu_instance_get(nrf_swi_t swi) { return (NRF_EGU_Type*) (NRF_EGU0_BASE + (((uint32_t) swi) * (NRF_EGU1_BASE - NRF_EGU0_BASE))); } /**@brief Software interrupt handler (using EGU). */ static void nrf_drv_swi_process(nrf_swi_t swi) { ASSERT(m_swi_handlers[swi - SWI_START_NUMBER]); nrf_swi_flags_t flags = 0; NRF_EGU_Type * NRF_EGUx = egu_instance_get(swi); for (uint8_t i = 0; i < NRF_EGU_CHANNEL_COUNT; ++i) { nrf_egu_event_t egu_event = nrf_egu_event_triggered_get(i); if (nrf_egu_event_check(NRF_EGUx, egu_event)) { flags |= (1u << i); nrf_egu_event_clear(NRF_EGUx, egu_event); } } m_swi_handlers[swi - SWI_START_NUMBER](swi, flags); } #define SWI_HANDLER_TEMPLATE(NUM) void SWI##NUM##_EGU##NUM##_IRQHandler(void) \ { \ nrf_drv_swi_process(NUM); \ } #else /**@brief Software interrupt handler (without EGU). */ static void nrf_drv_swi_process(nrf_swi_t swi, nrf_swi_flags_t flags) { ASSERT(m_swi_handlers[swi - SWI_START_NUMBER]); m_swi_flags[swi - SWI_START_NUMBER] &= ~flags; m_swi_handlers[swi - SWI_START_NUMBER](swi, flags); } #define SWI_HANDLER_TEMPLATE(NUM) void SWI##NUM##_IRQHandler(void) \ { \ nrf_drv_swi_process((NUM), m_swi_flags[(NUM) - SWI_START_NUMBER]); \ } #endif #if SWI_DISABLE0 == 0 SWI_HANDLER_TEMPLATE(0) #endif #if SWI_DISABLE1 == 0 SWI_HANDLER_TEMPLATE(1) #endif #if SWI_DISABLE2 == 0 SWI_HANDLER_TEMPLATE(2) #endif #if SWI_DISABLE3 == 0 SWI_HANDLER_TEMPLATE(3) #endif #if SWI_DISABLE4 == 0 SWI_HANDLER_TEMPLATE(4) #endif #if SWI_DISABLE5 == 0 SWI_HANDLER_TEMPLATE(5) #endif #define AVAILABLE_SWI (0x3FuL & ~( \ (SWI_DISABLE0 << 0) | (SWI_DISABLE1 << 1) | (SWI_DISABLE2 << 2) \ | (SWI_DISABLE3 << 3) | (SWI_DISABLE4 << 4) | (SWI_DISABLE5 << 5) \ )) #if (AVAILABLE_SWI == 0) #warning No available SWIs. #endif /**@brief Function for converting SWI number to system interrupt number. * * @param[in] swi SWI number. * * @retval IRQ number. */ __STATIC_INLINE IRQn_Type nrf_drv_swi_irq_of(nrf_swi_t swi) { return (IRQn_Type)((uint32_t)SWI0_IRQn + (uint32_t)swi); } /**@brief Function for checking if given SWI is allocated. * * @param[in] swi SWI number. */ __STATIC_INLINE bool swi_is_allocated(nrf_swi_t swi) { ASSERT(swi < SWI_COUNT); #if SWI_START_NUMBER > 0 if (swi < SWI_START_NUMBER) { return false; } #endif /*lint -e(661) out of range case handled by assert above*/ return m_swi_handlers[swi - SWI_START_NUMBER]; } ret_code_t nrf_drv_swi_init(void) { if (m_drv_state == NRF_DRV_STATE_UNINITIALIZED) { m_drv_state = NRF_DRV_STATE_INITIALIZED; return NRF_SUCCESS; } return MODULE_ALREADY_INITIALIZED; } void nrf_drv_swi_uninit(void) { ASSERT(m_drv_state != NRF_DRV_STATE_UNINITIALIZED) for (uint32_t i = SWI_START_NUMBER; i < SWI_COUNT; ++i) { m_swi_handlers[i - SWI_START_NUMBER] = NULL; nrf_drv_common_irq_disable(nrf_drv_swi_irq_of((nrf_swi_t) i)); #if EGU_ENABLED > 0 NRF_EGU_Type * NRF_EGUx = egu_instance_get(i); nrf_egu_int_disable(NRF_EGUx, NRF_EGU_INT_ALL); #endif } m_drv_state = NRF_DRV_STATE_UNINITIALIZED; return; } void nrf_drv_swi_free(nrf_swi_t * p_swi) { ASSERT(swi_is_allocated(*p_swi)); nrf_drv_common_irq_disable(nrf_drv_swi_irq_of(*p_swi)); m_swi_handlers[(*p_swi) - SWI_START_NUMBER] = NULL; *p_swi = NRF_SWI_UNALLOCATED; } ret_code_t nrf_drv_swi_alloc(nrf_swi_t * p_swi, nrf_swi_handler_t event_handler, uint32_t priority) { ASSERT(event_handler); uint32_t err_code = NRF_ERROR_NO_MEM; for (uint32_t i = SWI_START_NUMBER; i < SWI_COUNT; i++) { CRITICAL_REGION_ENTER(); if ((!swi_is_allocated(i)) && (AVAILABLE_SWI & (1 << i))) { m_swi_handlers[i - SWI_START_NUMBER] = event_handler; *p_swi = (nrf_swi_t) i; nrf_drv_common_irq_enable(nrf_drv_swi_irq_of(*p_swi), priority); #if EGU_ENABLED > 0 NRF_EGU_Type * NRF_EGUx = egu_instance_get(i); nrf_egu_int_enable(NRF_EGUx, NRF_EGU_INT_ALL); #endif err_code = NRF_SUCCESS; } CRITICAL_REGION_EXIT(); if (err_code == NRF_SUCCESS) { break; } } return err_code; } void nrf_drv_swi_trigger(nrf_swi_t swi, uint8_t flag_number) { ASSERT(swi_is_allocated((uint32_t) swi)); #if EGU_ENABLED > 0 ASSERT(flag_number < NRF_EGU_CHANNEL_COUNT); NRF_EGU_Type * NRF_EGUx = egu_instance_get(swi); nrf_egu_task_trigger(NRF_EGUx, nrf_egu_task_trigger_get(flag_number)); #else ASSERT(flag_number < SWI_MAX_FLAGS); m_swi_flags[swi - SWI_START_NUMBER] |= (1 << flag_number); NVIC_SetPendingIRQ(nrf_drv_swi_irq_of(swi)); #endif } #if EGU_ENABLED > 0 uint32_t nrf_drv_swi_task_trigger_address_get(nrf_swi_t swi, uint8_t channel) { NRF_EGU_Type * NRF_EGUx = egu_instance_get(swi); return (uint32_t) nrf_egu_task_trigger_addres_get(NRF_EGUx, channel); } uint32_t nrf_drv_swi_event_triggered_address_get(nrf_swi_t swi, uint8_t channel) { NRF_EGU_Type * NRF_EGUx = egu_instance_get(swi); return (uint32_t) nrf_egu_event_triggered_addres_get(NRF_EGUx, channel); } #endif