Boot Linux faster!

Check our new training course

Boot Linux faster!

Check our new training course
and Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

/*
 * Copyright (c) 2015 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @file
 * @brief Public API for SPI drivers and applications
 */

#if defined(CONFIG_SPI_LEGACY_API)

/*
 * This is the default, and will be the way until the new API below
 * will be enforced everywhere.
 */
#include <spi_legacy.h>

#else

#ifndef __SPI_H__
#define __SPI_H__

/**
 * @brief SPI Interface
 * @defgroup spi_interface SPI Interface
 * @ingroup io_interfaces
 * @{
 */

#include <zephyr/types.h>
#include <stddef.h>
#include <device.h>

#ifdef __cplusplus
extern "C" {
#endif

/**x
 * @brief SPI operational mode
 */
#define SPI_OP_MODE_MASTER	0
#define SPI_OP_MODE_SLAVE	1

/**
 * @brief SPI Polarity & Phase Modes
 */

/**
 * Clock Polarity: if set, clock idle state will be 1
 * and active state will be 0. If untouched, the inverse will be true
 * which is the default.
 */
#define SPI_MODE_CPOL		BIT(1)

/**
 * Clock Phase: this dictates when is the data captured, and depends
 * clock's polarity. When SPI_MODE_CPOL is set and this bit as well,
 * capture will occure on low to high transition and high to low if
 * this bit is not set (default). This is fully reversed if CPOL is
 * not set.
 */
#define SPI_MODE_CPHA		BIT(2)

/**
 * Whatever data is transmitted is looped-back to the receiving buffer of
 * the controller. This is fully controller dependent as some may not
 * support this, and can be used for testing purposes only.
 */
#define SPI_MODE_LOOP		BIT(3)

#define SPI_MODE_MASK		(0xE)
#define SPI_MODE_GET(_mode_)			\
	((_mode_) & SPI_MODE_MASK)

/**
 * @brief SPI Transfer modes (host controller dependent)
 */
#define SPI_TRANSFER_MSB	(0)
#define SPI_TRANSFER_LSB	BIT(4)

/**
 * @brief SPI word size
 */
#define SPI_WORD_SIZE_SHIFT	(5)
#define SPI_WORD_SIZE_MASK	(0x3F << SPI_WORD_SIZE_SHIFT)
#define SPI_WORD_SIZE_GET(_operation_)					\
	(((_operation_) & SPI_WORD_SIZE_MASK) >> SPI_WORD_SIZE_SHIFT)

#define SPI_WORD_SET(_word_size_)		\
	((_word_size_) << SPI_WORD_SIZE_SHIFT)

/**
 * @brief SPI MISO lines
 *
 * Some controllers support dual or quad MISO lines connected to slaves.
 * Default is single, which is the case most of the time.
 */
#define SPI_LINES_SINGLE	(0)
#define SPI_LINES_DUAL		BIT(11)
#define SPI_LINES_QUAD		BIT(12)

#define SPI_LINES_MASK		(0x3 << 11)

/**
 * @brief Specific SPI devices control bits
 */
/* Requests - if possible - to keep CS asserted after the transaction */
#define SPI_HOLD_ON_CS		BIT(13)
/* Keep the device locked after the transaction for the current config.
 * Use this with extreme caution (see spi_release() below) as it will
 * prevent other callers to access the SPI device until spi_release() is
 * properly called.
 */
#define SPI_LOCK_ON		BIT(14)
/* Select EEPROM read mode on master controller.
 * If not supported by the controller, the driver will have to emulate
 * the mode and thus should never return -EINVAL on that configuration)
 */
#define SPI_EEPROM_MODE		BIT(15)

/**
 * @brief SPI Chip Select control structure
 *
 * This can be used to control a CS line via a GPIO line, instead of
 * using the controller inner CS logic.
 *
 * gpio_dev is a valid pointer to an actual GPIO device
 * gpio_pin is a number representing the gpio PIN that will be used
 *    to act as a CS line
 * delay is a delay in microseconds to wait before starting the
 *    transmission and before releasing the CS line
 */
struct spi_cs_control {
	struct device	*gpio_dev;
	u32_t		gpio_pin;
	u32_t		delay;
};

/**
 * @brief SPI controller configuration structure
 *
 * dev is a valid pointer to an actual SPI device
 * frequency is the bus frequency in Hertz
 * operation is a bit field with the following parts:
 *    operational mode    [ 0 ]       - master or slave.
 *    mode                [ 1 : 3 ]   - Polarity, phase and loop mode.
 *    transfer            [ 4 ]       - LSB or MSB first.
 *    word_size           [ 5 : 10 ]  - Size of a data frame in bits.
 *    lines               [ 11 : 12 ] - MISO lines: Single/Dual/Quad.
 *    cs_hold             [ 13 ]      - Hold on the CS line if possible.
 *    lock_on             [ 14 ]      - Keep ressource locked for the caller.
 *    eeprom              [ 15 ]      - EEPROM mode.
 *
 * slave is the slave number from 0 to host constoller slave limit.
 *
 * cs is a valid pointer on a struct spi_cs_control is CS line is
 *    emulated through a gpio line, or NULL otherwise.
 *
 * Note: cs_hold, lock_on and eeprom_rx can be changed between consecutive
 * transceive call.
 */
struct spi_config {
	struct device	*dev;

	u32_t		frequency;
	u16_t		operation;
	u16_t		slave;

	struct spi_cs_control *cs;
};

/**
 * @brief SPI buffer structure
 *
 * buf is a valid pointer on a data buffer, or NULL otherwise.
 * len is the length of the buffer or, if buf is NULL, will be the
 *     length which as to be sent as dummy bytes (as TX buffer) or
 *     the length of bytes that should be skipped (as RX buffer).
 */
struct spi_buf {
	void *buf;
	size_t len;
};

/**
 * @typedef spi_api_io
 * @brief Callback API for I/O
 * See spi_transceive() for argument descriptions
 */
typedef int (*spi_api_io)(struct spi_config *config,
			  const struct spi_buf **tx_bufs,
			  struct spi_buf **rx_bufs);

/**
 * @typedef spi_api_io
 * @brief Callback API for asynchronous I/O
 * See spi_transceive_async() for argument descriptions
 */
typedef int (*spi_api_io_async)(struct spi_config *config,
				const struct spi_buf **tx_bufs,
				struct spi_buf **rx_bufs,
				struct k_poll_signal *async);

/**
 * @typedef spi_api_release
 * @brief Callback API for unlocking SPI device.
 * See spi_release() for argument descriptions
 */
typedef int (*spi_api_release)(struct spi_config *config);


/**
 * @brief SPI driver API
 * This is the mandatory API any SPI driver needs to expose.
 */
struct spi_driver_api {
	spi_api_io transceive;
#ifdef CONFIG_POLL
	spi_api_io_async transceive_async;
#endif
	spi_api_release release;
};

/**
 * @brief Read/write the specified amount of data from the SPI driver.
 *
 * Note: This function is synchronous.
 *
 * @param config Pointer to a valid spi_config structure instance.
 * @param tx_bufs NULL terminated buffer array where data to be sent
 *        originates from, or NULL if none.
 * @param rx_bufs NULL terminated buffer array where data to be read
 *        will be written to, or NULL if none.
 *
 * @retval 0 If successful, negative errno code otherwise.
 */
static inline int spi_transceive(struct spi_config *config,
				 const struct spi_buf **tx_bufs,
				 struct spi_buf **rx_bufs)
{
	const struct spi_driver_api *api = config->dev->driver_api;

	return api->transceive(config, tx_bufs, rx_bufs);
}

/**
 * @brief Read the specified amount of data from the SPI driver.
 *
 * Note: This function is synchronous.
 *
 * @param config Pointer to a valid spi_config structure instance.
 * @param rx_bufs NULL terminated buffer array where data to be read
 *        will be written to.
 *
 * @retval 0 If successful, negative errno code otherwise.
 */
static inline int spi_read(struct spi_config *config,
			   struct spi_buf **rx_bufs)
{
	const struct spi_driver_api *api = config->dev->driver_api;

	return api->transceive(config, NULL, rx_bufs);
}

/**
 * @brief Write the specified amount of data from the SPI driver.
 *
 * Note: This function is synchronous.
 *
 * @param config Pointer to a valid spi_config structure instance.
 * @param tx_bufs NULL terminated buffer array where data to be sent
 *        originates from.
 *
 * @retval 0 If successful, negative errno code otherwise.
 */
static inline int spi_write(struct spi_config *config,
			    const struct spi_buf **tx_bufs)
{
	const struct spi_driver_api *api = config->dev->driver_api;

	return api->transceive(config, tx_bufs, NULL);
}

#ifdef CONFIG_POLL
/**
 * @brief Read/write the specified amount of data from the SPI driver.
 *
 * Note: This function is asynchronous.
 *
 * @param config Pointer to a valid spi_config structure instance.
 * @param tx_bufs NULL terminated buffer array where data to be sent
 *        originates from, or NULL if none.
 * @param rx_bufs NULL terminated buffer array where data to be read
 *        will be written to, or NULL if none.
 * @param async A pointer to a valid and ready to be signaled
 *        struct k_poll_signal. (Note: if NULL this function will not
 *        notify the end of the transaction, and whether it went
 *        successfully or not).
 *
 * @retval 0 If successful, negative errno code otherwise.
 */
static inline int spi_transceive_async(struct spi_config *config,
				       const struct spi_buf **tx_bufs,
				       struct spi_buf **rx_bufs,
				       struct k_poll_signal *async)
{
	const struct spi_driver_api *api = config->dev->driver_api;

	return api->transceive_async(config, tx_bufs, rx_bufs, async);
}

/**
 * @brief Read the specified amount of data from the SPI driver.
 *
 * Note: This function is asynchronous.
 *
 * @param config Pointer to a valid spi_config structure instance.
 * @param rx_bufs NULL terminated buffer array where data to be read
 *        will be written to.
 * @param async A pointer to a valid and ready to be signaled
 *        struct k_poll_signal. (Note: if NULL this function will not
 *        notify the end of the transaction, and whether it went
 *        successfully or not).
 *
 * @retval 0 If successful, negative errno code otherwise.
 */
static inline int spi_read_async(struct spi_config *config,
				 struct spi_buf **rx_bufs,
				 struct k_poll_signal *async)
{
	const struct spi_driver_api *api = config->dev->driver_api;

	return api->transceive_async(config, NULL, rx_bufs, async);
}

/**
 * @brief Write the specified amount of data from the SPI driver.
 *
 * Note: This function is asynchronous.
 *
 * @param config Pointer to a valid spi_config structure instance.
 * @param tx_bufs NULL terminated buffer array where data to be sent
 *        originates from.
 * @param async A pointer to a valid and ready to be signaled
 *        struct k_poll_signal. (Note: if NULL this function will not
 *        notify the end of the transaction, and whether it went
 *        successfully or not).
 *
 * @retval 0 If successful, negative errno code otherwise.
 */
static inline int spi_write_async(struct spi_config *config,
				  const struct spi_buf **tx_bufs,
				  struct k_poll_signal *async)
{
	const struct spi_driver_api *api = config->dev->driver_api;

	return api->transceive_async(config, tx_bufs, NULL, async);
}
#endif /* CONFIG_POLL */

/**
 * @brief Release the SPI device locked on by the current config
 *
 * Note: This synchronous function is used to release the lock on the SPI
 *       device that was kept if, and if only, given config parameter was
 *       the last one to be used (in any of the above functions) and if
 *       it has the SPI_LOCK_ON bit set into its operation bits field.
 *       This can be used if the caller needs to keep its hand on the SPI
 *       device for consecutive transactions.
 *
 * @param config Pointer to a valid spi_config structure instance.
 */
static inline int spi_release(struct spi_config *config)
{
	const struct spi_driver_api *api = config->dev->driver_api;

	return api->release(config);
}

#ifdef __cplusplus
}
#endif

/**
 * @}
 */

#endif /* __SPI_H__ */

#endif /* CONFIG_SPI_LEGACY_API */