From 8eba36dd56b183984979e4eeabba8409ed751a54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20G=C5=82=C4=85bek?= Date: Thu, 19 Jan 2023 15:21:02 +0100 Subject: [PATCH] drivers: spi_nrfx_spi[m]: Handle transaction timeouts properly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add code that acts accordingly when a transaction does not complete in the expected time. It makes sure that the transaction is aborted so that no unexpected interrupt occurs afterwards and it also cleans up after that abort so that the driver can handle further requests. Signed-off-by: Andrzej Głąbek --- drivers/spi/spi_nrfx_spi.c | 50 +++++++++++++++++++++++++++++----- drivers/spi/spi_nrfx_spim.c | 53 ++++++++++++++++++++++++++++++++----- 2 files changed, 91 insertions(+), 12 deletions(-) diff --git a/drivers/spi/spi_nrfx_spi.c b/drivers/spi/spi_nrfx_spi.c index cdd24874123..3c9f52b7cf4 100644 --- a/drivers/spi/spi_nrfx_spi.c +++ b/drivers/spi/spi_nrfx_spi.c @@ -152,6 +152,19 @@ static int configure(const struct device *dev, return 0; } +static void finish_transaction(const struct device *dev, int error) +{ + struct spi_nrfx_data *dev_data = dev->data; + struct spi_context *ctx = &dev_data->ctx; + + spi_context_cs_control(ctx, false); + + LOG_DBG("Transaction finished with status %d", error); + + spi_context_complete(ctx, dev, error); + dev_data->busy = false; +} + static void transfer_next_chunk(const struct device *dev) { const struct spi_nrfx_config *dev_config = dev->config; @@ -179,12 +192,7 @@ static void transfer_next_chunk(const struct device *dev) error = -EIO; } - spi_context_cs_control(ctx, false); - - LOG_DBG("Transaction finished with status %d", error); - - spi_context_complete(ctx, dev, error); - dev_data->busy = false; + finish_transaction(dev, error); } static void event_handler(const nrfx_spi_evt_t *p_event, void *p_context) @@ -192,6 +200,14 @@ static void event_handler(const nrfx_spi_evt_t *p_event, void *p_context) struct spi_nrfx_data *dev_data = p_context; if (p_event->type == NRFX_SPI_EVENT_DONE) { + /* Chunk length is set to 0 when a transaction is aborted + * due to a timeout. + */ + if (dev_data->chunk_len == 0) { + finish_transaction(dev_data->dev, -ETIMEDOUT); + return; + } + spi_context_update_tx(&dev_data->ctx, 1, dev_data->chunk_len); spi_context_update_rx(&dev_data->ctx, 1, dev_data->chunk_len); @@ -208,6 +224,7 @@ static int transceive(const struct device *dev, void *userdata) { struct spi_nrfx_data *dev_data = dev->data; + const struct spi_nrfx_config *dev_config = dev->config; int error; spi_context_lock(&dev_data->ctx, asynchronous, cb, userdata, spi_cfg); @@ -222,6 +239,27 @@ static int transceive(const struct device *dev, transfer_next_chunk(dev); error = spi_context_wait_for_completion(&dev_data->ctx); + if (error == -ETIMEDOUT) { + /* Set the chunk length to 0 so that event_handler() + * knows that the transaction timed out and is to be + * aborted. + */ + dev_data->chunk_len = 0; + /* Abort the current transfer by deinitializing + * the nrfx driver. + */ + nrfx_spi_uninit(&dev_config->spi); + dev_data->initialized = false; + + /* Make sure the transaction is finished (it may be + * already finished if it actually did complete before + * the nrfx driver was deinitialized). + */ + finish_transaction(dev, -ETIMEDOUT); + + /* Clean up the driver state. */ + k_sem_reset(&dev_data->ctx.sync); + } } spi_context_release(&dev_data->ctx, error); diff --git a/drivers/spi/spi_nrfx_spim.c b/drivers/spi/spi_nrfx_spim.c index e8e976809a4..cd9da8069c5 100644 --- a/drivers/spi/spi_nrfx_spim.c +++ b/drivers/spi/spi_nrfx_spim.c @@ -285,6 +285,19 @@ static int anomaly_58_workaround_init(const struct device *dev) } #endif +static void finish_transaction(const struct device *dev, int error) +{ + struct spi_nrfx_data *dev_data = dev->data; + struct spi_context *ctx = &dev_data->ctx; + + spi_context_cs_control(ctx, false); + + LOG_DBG("Transaction finished with status %d", error); + + spi_context_complete(ctx, dev, error); + dev_data->busy = false; +} + static void transfer_next_chunk(const struct device *dev) { struct spi_nrfx_data *dev_data = dev->data; @@ -342,12 +355,7 @@ static void transfer_next_chunk(const struct device *dev) } } - spi_context_cs_control(ctx, false); - - LOG_DBG("Transaction finished with status %d", error); - - spi_context_complete(ctx, dev, error); - dev_data->busy = false; + finish_transaction(dev, error); } static void event_handler(const nrfx_spim_evt_t *p_event, void *p_context) @@ -355,6 +363,14 @@ static void event_handler(const nrfx_spim_evt_t *p_event, void *p_context) struct spi_nrfx_data *dev_data = p_context; if (p_event->type == NRFX_SPIM_EVENT_DONE) { + /* Chunk length is set to 0 when a transaction is aborted + * due to a timeout. + */ + if (dev_data->chunk_len == 0) { + finish_transaction(dev_data->dev, -ETIMEDOUT); + return; + } + #ifdef CONFIG_SOC_NRF52832_ALLOW_SPIM_DESPITE_PAN_58 anomaly_58_workaround_clear(dev_data); #endif @@ -374,6 +390,7 @@ static int transceive(const struct device *dev, void *userdata) { struct spi_nrfx_data *dev_data = dev->data; + const struct spi_nrfx_config *dev_config = dev->config; int error; spi_context_lock(&dev_data->ctx, asynchronous, cb, userdata, spi_cfg); @@ -388,6 +405,30 @@ static int transceive(const struct device *dev, transfer_next_chunk(dev); error = spi_context_wait_for_completion(&dev_data->ctx); + if (error == -ETIMEDOUT) { + /* Set the chunk length to 0 so that event_handler() + * knows that the transaction timed out and is to be + * aborted. + */ + dev_data->chunk_len = 0; + /* Abort the current transfer by deinitializing + * the nrfx driver. + */ + nrfx_spim_uninit(&dev_config->spim); + dev_data->initialized = false; + + /* Make sure the transaction is finished (it may be + * already finished if it actually did complete before + * the nrfx driver was deinitialized). + */ + finish_transaction(dev, -ETIMEDOUT); + + /* Clean up the driver state. */ + k_sem_reset(&dev_data->ctx.sync); +#ifdef CONFIG_SOC_NRF52832_ALLOW_SPIM_DESPITE_PAN_58 + anomaly_58_workaround_clear(dev_data); +#endif + } } spi_context_release(&dev_data->ctx, error);