Commit 8eba36dd authored by Andrzej Głąbek's avatar Andrzej Głąbek Committed by Martí Bolívar
Browse files

drivers: spi_nrfx_spi[m]: Handle transaction timeouts properly



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: default avatarAndrzej Głąbek <andrzej.glabek@nordicsemi.no>
parent 52ef8e44
Loading
Loading
Loading
Loading
+44 −6
Original line number Diff line number Diff line
@@ -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);
+47 −6
Original line number Diff line number Diff line
@@ -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);