nrfx_spis: Fix handling of anomaly 109 on nRF52

This patch provides the following changes related to the workaround
for anomaly 109 used in the nrfx_spis driver:
- allocation of the GPIOTE channel to be used is moved back to
  nrfx_spis_init() and when that allocation fails, the error code
  mentioned in documentation (NRFX_ERROR_INTERNAL) is returned
- the GPIOTE channel is now deinitialized and freed properly
- the CSN pin number is taken from the PSEL register in the SPIS
  peripheral, as in p_config that pin number may be omitted

Signed-off-by: Andrzej Głąbek <andrzej.glabek@nordicsemi.no>
This commit is contained in:
Andrzej Głąbek 2023-06-20 11:52:09 +02:00
parent a1c3e0fbaa
commit 87ed8765a0
1 changed files with 109 additions and 81 deletions

View File

@ -95,98 +95,52 @@ typedef struct
volatile nrfx_spis_state_t spi_state; //!< SPI slave state.
void * p_context; //!< Context set on initialization.
bool skip_gpio_cfg;
#if defined(USE_DMA_ISSUE_WORKAROUND)
uint32_t csn_pin;
uint8_t gpiote_ch;
#endif
} spis_cb_t;
static spis_cb_t m_cb[NRFX_SPIS_ENABLED_COUNT];
static nrfx_err_t pins_configure(nrfx_spis_config_t const * p_config)
static void pins_configure(nrfx_spis_config_t const * p_config)
{
if (!p_config->skip_gpio_cfg)
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);
#if NRF_GPIO_HAS_CLOCKPIN
nrfy_gpio_pin_clock_set(p_config->sck_pin, true);
#endif
if (p_config->mosi_pin != NRF_SPIS_PIN_NOT_CONNECTED)
{
nrf_gpio_cfg(p_config->sck_pin,
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);
#if NRF_GPIO_HAS_CLOCKPIN
nrfy_gpio_pin_clock_set(p_config->sck_pin, true);
#endif
if (p_config->mosi_pin != NRF_SPIS_PIN_NOT_CONNECTED)
{
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);
}
if (p_config->miso_pin != NRF_SPIS_PIN_NOT_CONNECTED)
{
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);
}
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);
#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(NRFX_GPIOTE_DEFAULT_CONFIG_IRQ_PRIORITY);
uint8_t ch;
nrfx_err_t err_code;
err_code = nrfx_gpiote_channel_alloc(&ch);
if (err_code != NRFX_SUCCESS)
{
NRFX_LOG_ERROR("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_gpiote_trigger_config_t trigger_config = {
.trigger = NRFX_GPIOTE_TRIGGER_HITOLO,
.p_in_channel = &ch
};
nrfx_gpiote_handler_config_t handler_config = {
.handler = csn_event_handler
};
err_code = nrfx_gpiote_input_configure(p_config->csn_pin,
NULL,
&trigger_config,
&handler_config);
if (err_code != NRFX_SUCCESS)
{
NRFX_LOG_ERROR("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
nrfx_gpiote_trigger_enable(p_config->csn_pin, true);
#endif
}
return NRFX_SUCCESS;
if (p_config->miso_pin != NRF_SPIS_PIN_NOT_CONNECTED)
{
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);
}
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);
}
static bool spis_configure(nrfx_spis_t const * p_instance,
@ -198,9 +152,9 @@ static bool spis_configure(nrfx_spis_t const * p_instance,
return false;
}
if (pins_configure(p_config) != NRFX_SUCCESS)
if (!p_config->skip_gpio_cfg)
{
return false;
pins_configure(p_config);
}
if (!p_config->skip_psel_cfg)
@ -212,6 +166,49 @@ static bool spis_configure(nrfx_spis_t const * p_instance,
p_config->csn_pin);
}
#if defined(USE_DMA_ISSUE_WORKAROUND)
spis_cb_t * p_cb = &m_cb[p_instance->drv_inst_idx];
// If the GPIOTE channel was already used with a CSN pin, deinitialize it
// first as that pin number may be different now.
if (p_cb->csn_pin != NRF_SPIS_PIN_NOT_CONNECTED)
{
nrfx_gpiote_pin_uninit(p_cb->csn_pin);
p_cb->csn_pin = NRF_SPIS_PIN_NOT_CONNECTED;
}
// Get the CSN pin number from the PSEL register in the peripheral
// as in p_config that pin number may be omitted.
uint32_t csn_pin = nrf_spis_csn_pin_get(p_spis);
// 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.
nrfx_gpiote_trigger_config_t trigger_config = {
.trigger = NRFX_GPIOTE_TRIGGER_HITOLO,
.p_in_channel = &p_cb->gpiote_ch
};
nrfx_gpiote_handler_config_t handler_config = {
.handler = csn_event_handler
};
nrfx_err_t err_code = nrfx_gpiote_input_configure(csn_pin,
NULL,
&trigger_config,
&handler_config);
if (err_code != NRFX_SUCCESS)
{
NRFX_LOG_ERROR("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return false;
}
nrfx_gpiote_trigger_enable(csn_pin, true);
p_cb->csn_pin = csn_pin;
#endif
// Configure SPI mode.
nrf_spis_configure(p_spis, p_config->mode, p_config->bit_order);
@ -271,12 +268,35 @@ nrfx_err_t nrfx_spis_init(nrfx_spis_t const * p_instance,
p_cb->handler = event_handler;
p_cb->p_context = p_context;
#if defined(USE_DMA_ISSUE_WORKAROUND)
p_cb->csn_pin = NRF_SPIS_PIN_NOT_CONNECTED;
// Allocate a GPIOTE channel that will be used to handle the anomaly 109
// (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 here).
(void)nrfx_gpiote_init(NRFX_GPIOTE_DEFAULT_CONFIG_IRQ_PRIORITY);
err_code = nrfx_gpiote_channel_alloc(&p_cb->gpiote_ch);
if (err_code != NRFX_SUCCESS)
{
err_code = NRFX_ERROR_INTERNAL;
NRFX_LOG_ERROR("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
#endif
if (p_config)
{
p_cb->skip_gpio_cfg = p_config->skip_gpio_cfg;
if (!spis_configure(p_instance, p_config))
{
#if defined(USE_DMA_ISSUE_WORKAROUND)
nrfx_gpiote_channel_free(p_cb->gpiote_ch);
#endif
err_code = NRFX_ERROR_INVALID_PARAM;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
@ -333,6 +353,14 @@ void nrfx_spis_uninit(nrfx_spis_t const * p_instance)
NRF_SPIS_Type * p_spis = p_instance->p_reg;
#if defined(USE_DMA_ISSUE_WORKAROUND)
if (p_cb->csn_pin != NRF_SPIS_PIN_NOT_CONNECTED)
{
nrfx_gpiote_pin_uninit(p_cb->csn_pin);
}
nrfx_gpiote_channel_free(p_cb->gpiote_ch);
#endif
#define DISABLE_ALL 0xFFFFFFFF
nrf_spis_disable(p_spis);
NRFX_IRQ_DISABLE(nrfx_get_irq_number(p_instance->p_reg));