Commit 0b843be9 authored by Declan Snyder's avatar Declan Snyder Committed by Benjamin Cabé
Browse files

drivers: spi_context: Add comments for context



Personally I found this file hard to understand at first,
but since now it is clear to me, I decided to put these comments
with my understanding to help anyone else who needs to use these.

Signed-off-by: default avatarDeclan Snyder <declan.snyder@nxp.com>
parent cccd9619
Loading
Loading
Loading
Loading
+108 −4
Original line number Diff line number Diff line
@@ -75,17 +75,30 @@ struct spi_context {
	},										\
	._ctx_name.num_cs_gpios = DT_PROP_LEN_OR(_node_id, cs_gpios, 0),

/*
 * Checks if a spi config is the same as the one stored in the spi_context
 * The intention of this function is to be used to check if a driver can skip
 * some reconfiguration for a transfer in a fast code path.
 */
static inline bool spi_context_configured(struct spi_context *ctx,
					  const struct spi_config *config)
{
	return !!(ctx->config == config);
}

/* Returns true if the spi configuration stored for this context
 * specifies a slave mode configuration, returns false otherwise
 */
static inline bool spi_context_is_slave(struct spi_context *ctx)
{
	return (ctx->config->operation & SPI_OP_MODE_SLAVE);
}

/*
 * The purpose of the context lock is to synchronize the usage of the driver/hardware.
 * The driver should call this function to claim or wait for ownership of the spi resource.
 * Usually the appropriate time to call this is at the start of the transceive API implementation.
 */
static inline void spi_context_lock(struct spi_context *ctx,
				    bool asynchronous,
				    spi_callback_t callback,
@@ -108,6 +121,13 @@ static inline void spi_context_lock(struct spi_context *ctx,
#endif /* CONFIG_SPI_ASYNC */
}

/*
 * This function must be called by a driver which has called spi_context_lock in order
 * to release the ownership of the spi resource.
 * Usually the appropriate time to call this would be at the end of a transfer that was
 * initiated by a transceive API call, except in the case that the SPI_LOCK_ON bit was set
 * in the configuration.
 */
static inline void spi_context_release(struct spi_context *ctx, int status)
{
#ifdef CONFIG_SPI_SLAVE
@@ -132,6 +152,13 @@ static inline void spi_context_release(struct spi_context *ctx, int status)
static inline size_t spi_context_total_tx_len(struct spi_context *ctx);
static inline size_t spi_context_total_rx_len(struct spi_context *ctx);

/* This function essentially is a way for a driver to seamlessly implement both the
 * synchronous transceive API and the asynchronous transceive_async API in the same way.
 *
 * The exact way this function is used may depend on driver implementation, but
 * essentially this will block waiting for a signal from spi_context_complete,
 * unless the transfer is asynchronous, in which case it does nothing in master mode.
 */
static inline int spi_context_wait_for_completion(struct spi_context *ctx)
{
	int status = 0;
@@ -180,6 +207,12 @@ static inline int spi_context_wait_for_completion(struct spi_context *ctx)
	return status;
}

/* For synchronous transfers, this will signal to a thread waiting
 * on spi_context_wait for completion.
 *
 * For asynchronous tranfers, this will call the async callback function
 * with the user data.
 */
static inline void spi_context_complete(struct spi_context *ctx,
					const struct device *dev,
					int status)
@@ -212,6 +245,14 @@ static inline void spi_context_complete(struct spi_context *ctx,
#endif /* CONFIG_SPI_ASYNC */
}

/*
 * This function initializes all the chip select GPIOs associated with a spi controller.
 * The context first must be initialized using the SPI_CONTEXT_CS_GPIOS_INITIALIZE macro.
 * This function should be called during the device init sequence so that
 * all the CS lines are configured properly before the first transfer begins.
 * Note: If a controller has native CS control in SPI hardware, they should also be initialized
 * during device init by the driver with hardware-specific code.
 */
static inline int spi_context_cs_configure_all(struct spi_context *ctx)
{
	int ret;
@@ -233,6 +274,7 @@ static inline int spi_context_cs_configure_all(struct spi_context *ctx)
	return 0;
}

/* Helper function to control the GPIO CS, not meant to be used directly by drivers */
static inline void _spi_context_cs_control(struct spi_context *ctx,
					   bool on, bool force_off)
{
@@ -252,11 +294,22 @@ static inline void _spi_context_cs_control(struct spi_context *ctx,
	}
}

/* This function should be called by drivers to control the chip select line in master mode
 * in the case of the CS being a GPIO. The de facto usage of the zephyr SPI API expects that the
 * chip select be asserted throughout the entire transfer specified by a transceive call,
 * ie all buffers in a spi_buf_set should be finished before deasserting CS. And usually
 * the deassertion is at the end of the transfer, except in the case that the
 * SPI_HOLD_ON_CS bit was set in the configuration.
 */
static inline void spi_context_cs_control(struct spi_context *ctx, bool on)
{
	_spi_context_cs_control(ctx, on, false);
}

/* Forcefully releases the spi context and removes the owner, allowing taking the lock
 * with spi_context_lock without the previous owner releasing the lock.
 * This is usually used to aid in implementation of the spi_release driver API.
 */
static inline void spi_context_unlock_unconditionally(struct spi_context *ctx)
{
	/* Forcing CS to go to inactive status */
@@ -268,6 +321,11 @@ static inline void spi_context_unlock_unconditionally(struct spi_context *ctx)
	}
}

/*
 * Helper function for incrementing buffer pointer.
 * Generally not needed to be used directly by drivers.
 * Use spi_context_update_(tx/rx) instead.
 */
static inline void *spi_context_get_next_buf(const struct spi_buf **current,
					     size_t *count,
					     size_t *buf_len,
@@ -287,6 +345,17 @@ static inline void *spi_context_get_next_buf(const struct spi_buf **current,
	return NULL;
}

/*
 * The spi context private api works with the driver by providing code to
 * keep track of how much of the transfer has been completed. The driver
 * calls functions to report when some tx or rx has finished, and the driver
 * then can use the spi context to keep track of how much is left to do.
 */

/*
 * This function must be called at the start of a transfer by the driver
 * to initialize the spi context fields for tracking the progress.
 */
static inline
void spi_context_buffers_setup(struct spi_context *ctx,
			       const struct spi_buf_set *tx_bufs,
@@ -322,8 +391,10 @@ void spi_context_buffers_setup(struct spi_context *ctx,
}

/*
 * Note: dfs is the number of bytes needed to store a data frame,
 * while len is the number of data frames sent.
 * Should be called to update the tracking of TX being completed.
 *
 * Parameter "dfs" is the number of bytes needed to store a data frame.
 * Parameter "len" is the number of data frames of TX that were sent.
 */
static ALWAYS_INLINE
void spi_context_update_tx(struct spi_context *ctx, uint8_t dfs, uint32_t len)
@@ -353,12 +424,18 @@ void spi_context_update_tx(struct spi_context *ctx, uint8_t dfs, uint32_t len)
	LOG_DBG("tx buf/len %p/%zu", (void *)ctx->tx_buf, ctx->tx_len);
}

/* Returns true if there is still TX buffers left in the spi_buf_set
 * even if they are "null" (nop) buffers.
 */
static ALWAYS_INLINE
bool spi_context_tx_on(struct spi_context *ctx)
{
	return !!(ctx->tx_len);
}

/* Similar to spi_context_tx_on, but only returns true if the current buffer is
 * not a null/NOP placeholder.
 */
static ALWAYS_INLINE
bool spi_context_tx_buf_on(struct spi_context *ctx)
{
@@ -366,8 +443,10 @@ bool spi_context_tx_buf_on(struct spi_context *ctx)
}

/*
 * Note: dfs is the number of bytes needed to store a data frame,
 * while len is the number of data frames received.
 * Should be called to update the tracking of RX being completed.
 *
 * @param dfs is the number of bytes needed to store a data frame.
 * @param len is the number of data frames of RX that were received.
 */
static ALWAYS_INLINE
void spi_context_update_rx(struct spi_context *ctx, uint8_t dfs, uint32_t len)
@@ -404,12 +483,18 @@ void spi_context_update_rx(struct spi_context *ctx, uint8_t dfs, uint32_t len)
	LOG_DBG("rx buf/len %p/%zu", (void *)ctx->rx_buf, ctx->rx_len);
}

/* Returns true if there is still RX buffers left in the spi_buf_set
 * even if they are "null" (nop) buffers.
 */
static ALWAYS_INLINE
bool spi_context_rx_on(struct spi_context *ctx)
{
	return !!(ctx->rx_len);
}

/* Similar to spi_context_rx_on, but only returns true if the current buffer is
 * not a null/NOP placeholder.
 */
static ALWAYS_INLINE
bool spi_context_rx_buf_on(struct spi_context *ctx)
{
@@ -420,6 +505,10 @@ bool spi_context_rx_buf_on(struct spi_context *ctx)
 * Returns the maximum length of a transfer for which all currently active
 * directions have a continuous buffer, i.e. the maximum SPI transfer that
 * can be done with DMA that handles only non-scattered buffers.
 *
 * In other words, returns the length of the smaller of the current RX or current TX buffer.
 * Except if either RX or TX buf length is 0, returns the length of the other.
 * And if both are 0 then will return 0 and should indicate transfer completion.
 */
static inline size_t spi_context_max_continuous_chunk(struct spi_context *ctx)
{
@@ -432,11 +521,13 @@ static inline size_t spi_context_max_continuous_chunk(struct spi_context *ctx)
	return MIN(ctx->tx_len, ctx->rx_len);
}

/* Returns the length of the longer of the current RX or current TX buffer. */
static inline size_t spi_context_longest_current_buf(struct spi_context *ctx)
{
	return ctx->tx_len > ctx->rx_len ? ctx->tx_len : ctx->rx_len;
}

/* Helper function, not intended to be used by drivers directly */
static size_t spi_context_count_tx_buf_lens(struct spi_context *ctx, size_t start_index)
{
	size_t n;
@@ -449,6 +540,7 @@ static size_t spi_context_count_tx_buf_lens(struct spi_context *ctx, size_t star
	return total_len;
}

/* Helper function, not intended to be used by drivers directly */
static size_t spi_context_count_rx_buf_lens(struct spi_context *ctx, size_t start_index)
{
	size_t n;
@@ -462,21 +554,33 @@ static size_t spi_context_count_rx_buf_lens(struct spi_context *ctx, size_t star
}


/* Returns the length of the sum of the remaining TX buffers in the buf set, including
 * the current buffer in the total.
 */
static inline size_t spi_context_total_tx_len(struct spi_context *ctx)
{
	return spi_context_count_tx_buf_lens(ctx, 0);
}

/* Returns the length of the sum of the remaining RX buffers in the buf set, including
 * the current buffer in the total.
 */
static inline size_t spi_context_total_rx_len(struct spi_context *ctx)
{
	return spi_context_count_rx_buf_lens(ctx, 0);
}

/* Similar to spi_context_total_tx_len, except does not count words that have been finished
 * in the current buffer, ie only including what is remaining in the current buffer in the sum.
 */
static inline size_t spi_context_tx_len_left(struct spi_context *ctx)
{
	return ctx->tx_len + spi_context_count_tx_buf_lens(ctx, 1);
}

/* Similar to spi_context_total_rx_len, except does not count words that have been finished
 * in the current buffer, ie only including what is remaining in the current buffer in the sum.
 */
static inline size_t spi_context_rx_len_left(struct spi_context *ctx)
{
	return ctx->rx_len + spi_context_count_rx_buf_lens(ctx, 1);