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:
parent
a1c3e0fbaa
commit
87ed8765a0
|
@ -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));
|
||||
|
|
Loading…
Reference in New Issue