drivers/spi: Add support for async call in DW driver in a generic way

All is done through the generic spi_context driver's API as it will be
generic to all SPI drivers.

Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
This commit is contained in:
Tomasz Bursztyka 2017-05-08 09:40:56 +02:00 committed by Anas Nashif
commit 587dd5d275
2 changed files with 71 additions and 12 deletions

View file

@ -25,6 +25,10 @@ struct spi_context {
struct k_sem lock; struct k_sem lock;
struct k_sem sync; struct k_sem sync;
#ifdef CONFIG_POLL
struct k_poll_signal *signal;
bool asynchronous;
#endif
const struct spi_buf **current_tx; const struct spi_buf **current_tx;
struct spi_buf **current_rx; struct spi_buf **current_rx;
@ -46,24 +50,55 @@ static inline bool spi_context_configured(struct spi_context *ctx,
return !!(ctx->config == config); return !!(ctx->config == config);
} }
static inline void spi_context_lock(struct spi_context *ctx) static inline void spi_context_lock(struct spi_context *ctx,
bool asynchronous,
struct k_poll_signal *signal)
{ {
k_sem_take(&ctx->lock, K_FOREVER); k_sem_take(&ctx->lock, K_FOREVER);
#ifdef CONFIG_POLL
ctx->asynchronous = asynchronous;
ctx->signal = signal;
#endif
} }
static inline void spi_context_release(struct spi_context *ctx) static inline void spi_context_release(struct spi_context *ctx, int status)
{ {
#ifdef CONFIG_POLL
if (!ctx->asynchronous || status) {
k_sem_give(&ctx->lock);
}
#else
k_sem_give(&ctx->lock); k_sem_give(&ctx->lock);
#endif
} }
static inline void spi_context_wait_for_completion(struct spi_context *ctx) static inline void spi_context_wait_for_completion(struct spi_context *ctx)
{ {
#ifdef CONFIG_POLL
if (!ctx->asynchronous) {
k_sem_take(&ctx->sync, K_FOREVER);
}
#else
k_sem_take(&ctx->sync, K_FOREVER); k_sem_take(&ctx->sync, K_FOREVER);
#endif
} }
static inline void spi_context_complete(struct spi_context *ctx) static inline void spi_context_complete(struct spi_context *ctx, int status)
{ {
#ifdef CONFIG_POLL
if (!ctx->asynchronous) {
k_sem_give(&ctx->sync);
} else {
if (ctx->signal) {
k_poll_signal(ctx->signal, status);
}
k_sem_give(&ctx->lock);
}
#else
k_sem_give(&ctx->sync); k_sem_give(&ctx->sync);
#endif
} }
static inline void spi_context_cs_configure(struct spi_context *ctx) static inline void spi_context_cs_configure(struct spi_context *ctx)

View file

@ -75,7 +75,7 @@ out:
SYS_LOG_DBG("SPI transaction completed %s error", SYS_LOG_DBG("SPI transaction completed %s error",
error ? "with" : "without"); error ? "with" : "without");
spi_context_complete(&spi->ctx); spi_context_complete(&spi->ctx, error ? -EIO : 0);
} }
static void push_data(struct device *dev) static void push_data(struct device *dev)
@ -250,9 +250,11 @@ static int spi_dw_configure(const struct spi_dw_config *info,
return 0; return 0;
} }
static int spi_dw_transceive(struct spi_config *config, static int transceive(struct spi_config *config,
const struct spi_buf **tx_bufs, const struct spi_buf **tx_bufs,
struct spi_buf **rx_bufs) struct spi_buf **rx_bufs,
bool asynchronous,
struct k_poll_signal *signal)
{ {
const struct spi_dw_config *info = config->dev->config->config_info; const struct spi_dw_config *info = config->dev->config->config_info;
struct spi_dw_data *spi = config->dev->driver_data; struct spi_dw_data *spi = config->dev->driver_data;
@ -260,15 +262,13 @@ static int spi_dw_transceive(struct spi_config *config,
u32_t imask = DW_SPI_IMR_UNMASK; u32_t imask = DW_SPI_IMR_UNMASK;
int ret = 0; int ret = 0;
SYS_LOG_DBG("%p, %p, %p", config->dev, tx_bufs, rx_bufs);
/* Check status */ /* Check status */
if (test_bit_ssienr(info->regs) || test_bit_sr_busy(info->regs)) { if (test_bit_ssienr(info->regs) || test_bit_sr_busy(info->regs)) {
SYS_LOG_DBG("Controller is busy"); SYS_LOG_DBG("Controller is busy");
return -EBUSY; return -EBUSY;
} }
spi_context_lock(&spi->ctx); spi_context_lock(&spi->ctx, asynchronous, signal);
/* Configure */ /* Configure */
ret = spi_dw_configure(info, spi, config); ret = spi_dw_configure(info, spi, config);
@ -311,11 +311,32 @@ static int spi_dw_transceive(struct spi_config *config,
ret = -EIO; ret = -EIO;
} }
out: out:
spi_context_release(&spi->ctx); spi_context_release(&spi->ctx, ret);
return ret; return ret;
} }
static int spi_dw_transceive(struct spi_config *config,
const struct spi_buf **tx_bufs,
struct spi_buf **rx_bufs)
{
SYS_LOG_DBG("%p, %p, %p", config->dev, tx_bufs, rx_bufs);
return transceive(config, tx_bufs, rx_bufs, false, NULL);
}
#ifdef CONFIG_POLL
static int spi_dw_transceive_async(struct spi_config *config,
const struct spi_buf **tx_bufs,
struct spi_buf **rx_bufs,
struct k_poll_signal *async)
{
SYS_LOG_DBG("%p, %p, %p, %p", config->dev, tx_bufs, rx_bufs, async);
return transceive(config, tx_bufs, rx_bufs, true, async);
}
#endif /* CONFIG_POLL */
void spi_dw_isr(struct device *dev) void spi_dw_isr(struct device *dev)
{ {
const struct spi_dw_config *info = dev->config->config_info; const struct spi_dw_config *info = dev->config->config_info;
@ -349,6 +370,9 @@ out:
static const struct spi_driver_api dw_spi_api = { static const struct spi_driver_api dw_spi_api = {
.transceive = spi_dw_transceive, .transceive = spi_dw_transceive,
#ifdef CONFIG_POLL
.transceive_async = spi_dw_transceive_async,
#endif
}; };
int spi_dw_init(struct device *dev) int spi_dw_init(struct device *dev)
@ -367,7 +391,7 @@ int spi_dw_init(struct device *dev)
SYS_LOG_DBG("Designware SPI driver initialized on device: %p", dev); SYS_LOG_DBG("Designware SPI driver initialized on device: %p", dev);
spi_context_release(&spi->ctx); spi_context_release(&spi->ctx, 0);
return 0; return 0;
} }