diff options
Diffstat (limited to 'thirdparty/nRF5_SDK_15.0.0_a53641a/modules/nrfx/drivers/src/nrfx_spis.c')
-rw-r--r-- | thirdparty/nRF5_SDK_15.0.0_a53641a/modules/nrfx/drivers/src/nrfx_spis.c | 494 |
1 files changed, 494 insertions, 0 deletions
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/modules/nrfx/drivers/src/nrfx_spis.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/modules/nrfx/drivers/src/nrfx_spis.c new file mode 100644 index 0000000..53075f0 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/modules/nrfx/drivers/src/nrfx_spis.c @@ -0,0 +1,494 @@ +/** + * Copyright (c) 2013 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <nrfx.h> + +#if NRFX_CHECK(NRFX_SPIS_ENABLED) + +#if !(NRFX_CHECK(NRFX_SPIS0_ENABLED) || NRFX_CHECK(NRFX_SPIS1_ENABLED) || \ + NRFX_CHECK(NRFX_SPIS2_ENABLED)) +#error "No enabled SPIS instances. Check <nrfx_config.h>." +#endif + +#include <nrfx_spis.h> +#include "prs/nrfx_prs.h" + +#define NRFX_LOG_MODULE SPIS +#include <nrfx_log.h> + +#define EVT_TO_STR(event) \ + (event == NRF_SPIS_EVENT_ACQUIRED ? "NRF_SPIS_EVENT_ACQUIRED" : \ + (event == NRF_SPIS_EVENT_END ? "NRF_SPIS_EVENT_END" : \ + "UNKNOWN ERROR")) + +#define SPISX_LENGTH_VALIDATE(peripheral, drv_inst_idx, rx_len, tx_len) \ + (((drv_inst_idx) == NRFX_CONCAT_3(NRFX_, peripheral, _INST_IDX)) && \ + NRFX_EASYDMA_LENGTH_VALIDATE(peripheral, rx_len, tx_len)) + +#if NRFX_CHECK(NRFX_SPIS0_ENABLED) +#define SPIS0_LENGTH_VALIDATE(...) SPISX_LENGTH_VALIDATE(SPIS0, __VA_ARGS__) +#else +#define SPIS0_LENGTH_VALIDATE(...) 0 +#endif + +#if NRFX_CHECK(NRFX_SPIS1_ENABLED) +#define SPIS1_LENGTH_VALIDATE(...) SPISX_LENGTH_VALIDATE(SPIS1, __VA_ARGS__) +#else +#define SPIS1_LENGTH_VALIDATE(...) 0 +#endif + +#if NRFX_CHECK(NRFX_SPIS2_ENABLED) +#define SPIS2_LENGTH_VALIDATE(...) SPISX_LENGTH_VALIDATE(SPIS2, __VA_ARGS__) +#else +#define SPIS2_LENGTH_VALIDATE(...) 0 +#endif + +#define SPIS_LENGTH_VALIDATE(drv_inst_idx, rx_len, tx_len) \ + (SPIS0_LENGTH_VALIDATE(drv_inst_idx, rx_len, tx_len) || \ + SPIS1_LENGTH_VALIDATE(drv_inst_idx, rx_len, tx_len) || \ + SPIS2_LENGTH_VALIDATE(drv_inst_idx, rx_len, tx_len)) + + +#if NRFX_CHECK(NRFX_SPIS_NRF52_ANOMALY_109_WORKAROUND_ENABLED) +#include <nrfx_gpiote.h> +#define USE_DMA_ISSUE_WORKAROUND +// This handler is called by the GPIOTE driver when a falling edge is detected +// on the CSN line. There is no need to do anything here. The handling of the +// interrupt itself provides a protection for DMA transfers. +static void csn_event_handler(nrfx_gpiote_pin_t pin, + nrf_gpiote_polarity_t action) +{ +} +#endif + + +/**@brief States of the SPI transaction state machine. */ +typedef enum +{ + SPIS_STATE_INIT, /**< Initialization state. In this state the module waits for a call to @ref spi_slave_buffers_set. */ + SPIS_BUFFER_RESOURCE_REQUESTED, /**< State where the configuration of the memory buffers, which are to be used in SPI transaction, has started. */ + SPIS_BUFFER_RESOURCE_CONFIGURED, /**< State where the configuration of the memory buffers, which are to be used in SPI transaction, has completed. */ + SPIS_XFER_COMPLETED /**< State where SPI transaction has been completed. */ +} nrfx_spis_state_t; + +/**@brief SPIS control block - driver instance local data. */ +typedef struct +{ + volatile uint32_t tx_buffer_size; //!< SPI slave TX buffer size in bytes. + volatile uint32_t rx_buffer_size; //!< SPI slave RX buffer size in bytes. + nrfx_spis_event_handler_t handler; //!< SPI event handler. + volatile const uint8_t * tx_buffer; //!< SPI slave TX buffer. + volatile uint8_t * rx_buffer; //!< SPI slave RX buffer. + nrfx_drv_state_t state; //!< driver initialization state. + volatile nrfx_spis_state_t spi_state; //!< SPI slave state. + void * p_context; //!< Context set on initialization. +} spis_cb_t; + +static spis_cb_t m_cb[NRFX_SPIS_ENABLED_COUNT]; + +nrfx_err_t nrfx_spis_init(nrfx_spis_t const * const p_instance, + nrfx_spis_config_t const * p_config, + nrfx_spis_event_handler_t event_handler, + void * p_context) +{ + NRFX_ASSERT(p_config); + NRFX_ASSERT(event_handler); + spis_cb_t * p_cb = &m_cb[p_instance->drv_inst_idx]; + nrfx_err_t err_code; + + NRF_SPIS_Type * p_spis = p_instance->p_reg; + + if (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED) + { + err_code = NRFX_ERROR_INVALID_STATE; + NRFX_LOG_WARNING("Function: %s, error code: %s.", + __func__, + NRFX_LOG_ERROR_STRING_GET(err_code)); + return err_code; + } + + if ((uint32_t)p_config->mode > (uint32_t)NRF_SPIS_MODE_3) + { + err_code = NRFX_ERROR_INVALID_PARAM; + NRFX_LOG_WARNING("Function: %s, error code: %s.", + __func__, + NRFX_LOG_ERROR_STRING_GET(err_code)); + return err_code; + } +#if NRFX_CHECK(NRFX_PRS_ENABLED) + static nrfx_irq_handler_t const irq_handlers[NRFX_SPIS_ENABLED_COUNT] = { + #if NRFX_CHECK(NRFX_SPIS0_ENABLED) + nrfx_spis_0_irq_handler, + #endif + #if NRFX_CHECK(NRFX_SPIS1_ENABLED) + nrfx_spis_1_irq_handler, + #endif + #if NRFX_CHECK(NRFX_SPIS2_ENABLED) + nrfx_spis_2_irq_handler, + #endif + }; + if (nrfx_prs_acquire(p_spis, + irq_handlers[p_instance->drv_inst_idx]) != NRFX_SUCCESS) + { + err_code = NRFX_ERROR_BUSY; + NRFX_LOG_WARNING("Function: %s, error code: %s.", + __func__, + NRFX_LOG_ERROR_STRING_GET(err_code)); + return err_code; + } +#endif // NRFX_CHECK(NRFX_PRS_ENABLED) + + // Configure the SPI pins for input. + uint32_t mosi_pin; + uint32_t miso_pin; + + if (p_config->miso_pin != NRFX_SPIS_PIN_NOT_USED) + { + nrf_gpio_cfg(p_config->miso_pin, + NRF_GPIO_PIN_DIR_INPUT, + NRF_GPIO_PIN_INPUT_CONNECT, + NRF_GPIO_PIN_NOPULL, + p_config->miso_drive, + NRF_GPIO_PIN_NOSENSE); + miso_pin = p_config->miso_pin; + } + else + { + miso_pin = NRF_SPIS_PIN_NOT_CONNECTED; + } + + if (p_config->mosi_pin != NRFX_SPIS_PIN_NOT_USED) + { + nrf_gpio_cfg(p_config->mosi_pin, + NRF_GPIO_PIN_DIR_INPUT, + NRF_GPIO_PIN_INPUT_CONNECT, + NRF_GPIO_PIN_NOPULL, + NRF_GPIO_PIN_S0S1, + NRF_GPIO_PIN_NOSENSE); + mosi_pin = p_config->mosi_pin; + } + else + { + mosi_pin = NRF_SPIS_PIN_NOT_CONNECTED; + } + + nrf_gpio_cfg(p_config->csn_pin, + NRF_GPIO_PIN_DIR_INPUT, + NRF_GPIO_PIN_INPUT_CONNECT, + p_config->csn_pullup, + NRF_GPIO_PIN_S0S1, + NRF_GPIO_PIN_NOSENSE); + + nrf_gpio_cfg(p_config->sck_pin, + NRF_GPIO_PIN_DIR_INPUT, + NRF_GPIO_PIN_INPUT_CONNECT, + NRF_GPIO_PIN_NOPULL, + NRF_GPIO_PIN_S0S1, + NRF_GPIO_PIN_NOSENSE); + + nrf_spis_pins_set(p_spis, p_config->sck_pin, mosi_pin, miso_pin, p_config->csn_pin); + + nrf_spis_rx_buffer_set(p_spis, NULL, 0); + nrf_spis_tx_buffer_set(p_spis, NULL, 0); + + // Configure SPI mode. + nrf_spis_configure(p_spis, p_config->mode, p_config->bit_order); + + // Configure DEF and ORC characters. + nrf_spis_def_set(p_spis, p_config->def); + nrf_spis_orc_set(p_spis, p_config->orc); + + // Clear possible pending events. + nrf_spis_event_clear(p_spis, NRF_SPIS_EVENT_END); + nrf_spis_event_clear(p_spis, NRF_SPIS_EVENT_ACQUIRED); + + // Enable END_ACQUIRE shortcut. + nrf_spis_shorts_enable(p_spis, NRF_SPIS_SHORT_END_ACQUIRE); + + p_cb->spi_state = SPIS_STATE_INIT; + p_cb->handler = event_handler; + p_cb->p_context = p_context; + +#if defined(USE_DMA_ISSUE_WORKAROUND) + // Configure a GPIOTE channel to generate interrupts on each falling edge + // on the CSN line. Handling of these interrupts will make the CPU active, + // and thus will protect the DMA transfers started by SPIS right after it + // is selected for communication. + // [the GPIOTE driver may be already initialized at this point (by this + // driver when another SPIS instance is used, or by an application code), + // so just ignore the returned value] + (void)nrfx_gpiote_init(); + static nrfx_gpiote_in_config_t const csn_gpiote_config = + NRFX_GPIOTE_CONFIG_IN_SENSE_HITOLO(true); + nrfx_err_t gpiote_err_code = nrfx_gpiote_in_init(p_config->csn_pin, + &csn_gpiote_config, csn_event_handler); + if (gpiote_err_code != NRFX_SUCCESS) + { + err_code = NRFX_ERROR_INTERNAL; + NRFX_LOG_INFO("Function: %s, error code: %s.", + __func__, + NRFX_LOG_ERROR_STRING_GET(err_code)); + return err_code; + } + nrfx_gpiote_in_event_enable(p_config->csn_pin, true); +#endif + + // Enable IRQ. + nrf_spis_int_enable(p_spis, NRF_SPIS_INT_ACQUIRED_MASK | + NRF_SPIS_INT_END_MASK); + NRFX_IRQ_PRIORITY_SET(nrfx_get_irq_number(p_instance->p_reg), + p_config->irq_priority); + NRFX_IRQ_ENABLE(nrfx_get_irq_number(p_instance->p_reg)); + + p_cb->state = NRFX_DRV_STATE_INITIALIZED; + + // Enable SPI slave device. + nrf_spis_enable(p_spis); + + NRFX_LOG_INFO("Initialized."); + return NRFX_SUCCESS; +} + + +void nrfx_spis_uninit(nrfx_spis_t const * const p_instance) +{ + spis_cb_t * p_cb = &m_cb[p_instance->drv_inst_idx]; + NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED); + + NRF_SPIS_Type * p_spis = p_instance->p_reg; + + #define DISABLE_ALL 0xFFFFFFFF + nrf_spis_disable(p_spis); + NRFX_IRQ_DISABLE(nrfx_get_irq_number(p_instance->p_reg)); + nrf_spis_int_disable(p_spis, DISABLE_ALL); + #undef DISABLE_ALL + +#if NRFX_CHECK(NRFX_PRS_ENABLED) + nrfx_prs_release(p_spis); +#endif + + p_cb->state = NRFX_DRV_STATE_UNINITIALIZED; + NRFX_LOG_INFO("Uninitialized."); +} + + +/**@brief Function for executing the state entry action. */ +static void spis_state_entry_action_execute(NRF_SPIS_Type * p_spis, + spis_cb_t * p_cb) +{ + nrfx_spis_evt_t event; + + switch (p_cb->spi_state) + { + case SPIS_BUFFER_RESOURCE_REQUESTED: + nrf_spis_task_trigger(p_spis, NRF_SPIS_TASK_ACQUIRE); + break; + + case SPIS_BUFFER_RESOURCE_CONFIGURED: + event.evt_type = NRFX_SPIS_BUFFERS_SET_DONE; + event.rx_amount = 0; + event.tx_amount = 0; + + NRFX_ASSERT(p_cb->handler != NULL); + p_cb->handler(&event, p_cb->p_context); + break; + + case SPIS_XFER_COMPLETED: + event.evt_type = NRFX_SPIS_XFER_DONE; + event.rx_amount = nrf_spis_rx_amount_get(p_spis); + event.tx_amount = nrf_spis_tx_amount_get(p_spis); + NRFX_LOG_INFO("Transfer rx_len:%d.", event.rx_amount); + NRFX_LOG_DEBUG("Rx data:"); + NRFX_LOG_HEXDUMP_DEBUG((uint8_t const *)p_cb->rx_buffer, + event.rx_amount * sizeof(p_cb->rx_buffer[0])); + NRFX_ASSERT(p_cb->handler != NULL); + p_cb->handler(&event, p_cb->p_context); + break; + + default: + // No implementation required. + break; + } +} + +/**@brief Function for changing the state of the SPI state machine. + * + * @param[in] p_spis SPIS instance register. + * @param[in] p_cb SPIS instance control block. + * @param[in] new_state State where the state machine transits to. + */ +static void spis_state_change(NRF_SPIS_Type * p_spis, + spis_cb_t * p_cb, + nrfx_spis_state_t new_state) +{ + p_cb->spi_state = new_state; + spis_state_entry_action_execute(p_spis, p_cb); +} + +nrfx_err_t nrfx_spis_buffers_set(nrfx_spis_t const * const p_instance, + uint8_t const * p_tx_buffer, + size_t tx_buffer_length, + uint8_t * p_rx_buffer, + size_t rx_buffer_length) +{ + NRFX_ASSERT(p_tx_buffer != NULL || tx_buffer_length == 0); + NRFX_ASSERT(p_rx_buffer != NULL || rx_buffer_length == 0); + + spis_cb_t * p_cb = &m_cb[p_instance->drv_inst_idx]; + nrfx_err_t err_code; + + if (!SPIS_LENGTH_VALIDATE(p_instance->drv_inst_idx, + rx_buffer_length, + tx_buffer_length)) + { + return NRFX_ERROR_INVALID_LENGTH; + } + + // EasyDMA requires that transfer buffers are placed in Data RAM region; + // signal error if they are not. + if ((p_tx_buffer != NULL && !nrfx_is_in_ram(p_tx_buffer)) || + (p_rx_buffer != NULL && !nrfx_is_in_ram(p_rx_buffer))) + { + err_code = NRFX_ERROR_INVALID_ADDR; + NRFX_LOG_WARNING("Function: %s, error code: %s.", + __func__, + NRFX_LOG_ERROR_STRING_GET(err_code)); + return err_code; + } + + switch (p_cb->spi_state) + { + case SPIS_STATE_INIT: + case SPIS_XFER_COMPLETED: + case SPIS_BUFFER_RESOURCE_CONFIGURED: + p_cb->tx_buffer = p_tx_buffer; + p_cb->rx_buffer = p_rx_buffer; + p_cb->tx_buffer_size = tx_buffer_length; + p_cb->rx_buffer_size = rx_buffer_length; + err_code = NRFX_SUCCESS; + + spis_state_change(p_instance->p_reg, p_cb, SPIS_BUFFER_RESOURCE_REQUESTED); + break; + + case SPIS_BUFFER_RESOURCE_REQUESTED: + err_code = NRFX_ERROR_INVALID_STATE; + break; + + default: + // @note: execution of this code path would imply internal error in the design. + err_code = NRFX_ERROR_INTERNAL; + break; + } + + NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code)); + return err_code; +} + +static void spis_irq_handler(NRF_SPIS_Type * p_spis, spis_cb_t * p_cb) +{ + // @note: as multiple events can be pending for processing, the correct event processing order + // is as follows: + // - SPI semaphore acquired event. + // - SPI transaction complete event. + + // Check for SPI semaphore acquired event. + if (nrf_spis_event_check(p_spis, NRF_SPIS_EVENT_ACQUIRED)) + { + nrf_spis_event_clear(p_spis, NRF_SPIS_EVENT_ACQUIRED); + NRFX_LOG_DEBUG("SPIS: Event: %s.", EVT_TO_STR(NRF_SPIS_EVENT_ACQUIRED)); + + switch (p_cb->spi_state) + { + case SPIS_BUFFER_RESOURCE_REQUESTED: + nrf_spis_tx_buffer_set(p_spis, (uint8_t *)p_cb->tx_buffer, p_cb->tx_buffer_size); + nrf_spis_rx_buffer_set(p_spis, (uint8_t *)p_cb->rx_buffer, p_cb->rx_buffer_size); + + nrf_spis_task_trigger(p_spis, NRF_SPIS_TASK_RELEASE); + + spis_state_change(p_spis, p_cb, SPIS_BUFFER_RESOURCE_CONFIGURED); + break; + + default: + // No implementation required. + break; + } + } + + // Check for SPI transaction complete event. + if (nrf_spis_event_check(p_spis, NRF_SPIS_EVENT_END)) + { + nrf_spis_event_clear(p_spis, NRF_SPIS_EVENT_END); + NRFX_LOG_DEBUG("SPIS: Event: %s.", EVT_TO_STR(NRF_SPIS_EVENT_END)); + + switch (p_cb->spi_state) + { + case SPIS_BUFFER_RESOURCE_CONFIGURED: + spis_state_change(p_spis, p_cb, SPIS_XFER_COMPLETED); + break; + + default: + // No implementation required. + break; + } + } +} + +#if NRFX_CHECK(NRFX_SPIS0_ENABLED) +void nrfx_spis_0_irq_handler(void) +{ + spis_irq_handler(NRF_SPIS0, &m_cb[NRFX_SPIS0_INST_IDX]); +} +#endif + +#if NRFX_CHECK(NRFX_SPIS1_ENABLED) +void nrfx_spis_1_irq_handler(void) +{ + spis_irq_handler(NRF_SPIS1, &m_cb[NRFX_SPIS1_INST_IDX]); +} +#endif + +#if NRFX_CHECK(NRFX_SPIS2_ENABLED) +void nrfx_spis_2_irq_handler(void) +{ + spis_irq_handler(NRF_SPIS2, &m_cb[NRFX_SPIS2_INST_IDX]); +} +#endif + +#endif // NRFX_CHECK(NRFX_SPIS_ENABLED) |