blob: f2ee333286f0e14d570fd298e5f70cc2552531cc [file] [log] [blame]
/*
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Private API for SPI drivers
*/
#ifndef __SPI_DRIVER_COMMON_H__
#define __SPI_DRIVER_COMMON_H__
#include <gpio.h>
#include <spi.h>
#ifdef __cplusplus
extern "C" {
#endif
struct spi_context {
struct spi_config *config;
struct k_sem lock;
struct k_sem sync;
#ifdef CONFIG_POLL
struct k_poll_signal *signal;
bool asynchronous;
#endif
const struct spi_buf **current_tx;
struct spi_buf **current_rx;
void *tx_buf;
u32_t tx_len;
void *rx_buf;
u32_t rx_len;
};
#define SPI_CONTEXT_INIT_LOCK(_data, _ctx_name) \
._ctx_name.lock = K_SEM_INITIALIZER(_data._ctx_name.lock, 0, 1)
#define SPI_CONTEXT_INIT_SYNC(_data, _ctx_name) \
._ctx_name.sync = K_SEM_INITIALIZER(_data._ctx_name.sync, 0, UINT_MAX)
static inline bool spi_context_configured(struct spi_context *ctx,
struct spi_config *config)
{
return !!(ctx->config == config);
}
static inline void spi_context_lock(struct spi_context *ctx,
bool asynchronous,
struct k_poll_signal *signal)
{
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, int status)
{
if (!status && (ctx->config->operation & SPI_LOCK_ON)) {
return;
}
#ifdef CONFIG_POLL
if (!ctx->asynchronous || status) {
k_sem_give(&ctx->lock);
}
#else
k_sem_give(&ctx->lock);
#endif
}
static inline void spi_context_unlock_unconditionally(struct spi_context *ctx)
{
if (!k_sem_count_get(&ctx->lock)) {
k_sem_give(&ctx->lock);
}
}
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);
#endif
}
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);
}
if (!(ctx->config->operation & SPI_LOCK_ON)) {
k_sem_give(&ctx->lock);
}
}
#else
k_sem_give(&ctx->sync);
#endif
}
static inline void spi_context_cs_configure(struct spi_context *ctx)
{
if (ctx->config->cs) {
gpio_pin_configure(ctx->config->cs->gpio_dev,
ctx->config->cs->gpio_pin, GPIO_DIR_OUT);
gpio_pin_write(ctx->config->cs->gpio_dev,
ctx->config->cs->gpio_pin, 1);
}
}
static inline void spi_context_cs_control(struct spi_context *ctx, bool on)
{
if (ctx->config->cs) {
if (on) {
gpio_pin_write(ctx->config->cs->gpio_dev,
ctx->config->cs->gpio_pin, 0);
k_busy_wait(ctx->config->cs->delay);
} else {
if (ctx->config->operation & SPI_HOLD_ON_CS) {
return;
}
k_busy_wait(ctx->config->cs->delay);
gpio_pin_write(ctx->config->cs->gpio_dev,
ctx->config->cs->gpio_pin, 1);
}
}
}
static inline void spi_context_buffers_setup(struct spi_context *ctx,
const struct spi_buf **tx_bufs,
struct spi_buf **rx_bufs,
uint8_t dfs)
{
SYS_LOG_DBG("tx_bufs %p (%p) - rx_bufs %p (%p) - %u",
tx_bufs, tx_bufs ? *tx_bufs : NULL,
rx_bufs, rx_bufs ? *rx_bufs : NULL, dfs);
ctx->current_tx = tx_bufs;
ctx->current_rx = rx_bufs;
if (*tx_bufs) {
ctx->tx_buf = (*tx_bufs)->buf;
ctx->tx_len = (*tx_bufs)->len/dfs;
} else {
ctx->tx_buf = NULL;
ctx->tx_len = 0;
}
if (*rx_bufs) {
ctx->rx_buf = (*rx_bufs)->buf;
ctx->rx_len = (*rx_bufs)->len/dfs;
} else {
ctx->rx_buf = NULL;
ctx->rx_len = 0;
}
SYS_LOG_DBG("current_tx %p, current_rx %p,"
" tx buf/len %p/%u, rx buf/len %p/%u",
ctx->current_tx, ctx->current_rx,
ctx->tx_buf, ctx->tx_len, ctx->rx_buf, ctx->rx_len);
}
static ALWAYS_INLINE
void spi_context_update_tx(struct spi_context *ctx, uint8_t dfs)
{
if (!ctx->tx_len) {
return;
}
ctx->tx_len--;
if (!ctx->tx_len) {
ctx->current_tx++;
if (*ctx->current_tx) {
ctx->tx_buf = (*ctx->current_tx)->buf;
ctx->tx_len = (*ctx->current_tx)->len/dfs;
} else {
ctx->tx_buf = NULL;
}
} else if (ctx->tx_buf) {
ctx->tx_buf += dfs;
}
SYS_LOG_DBG("tx buf/len %p/%u", ctx->tx_buf, ctx->tx_len);
}
static ALWAYS_INLINE
bool spi_context_tx_on(struct spi_context *ctx)
{
return !!(ctx->tx_buf || ctx->tx_len);
}
static ALWAYS_INLINE
void spi_context_update_rx(struct spi_context *ctx, uint8_t dfs)
{
if (!ctx->rx_len) {
return;
}
ctx->rx_len--;
if (!ctx->rx_len) {
ctx->current_rx++;
if (*ctx->current_rx) {
ctx->rx_buf = (*ctx->current_rx)->buf;
ctx->rx_len = (*ctx->current_rx)->len/dfs;
} else {
ctx->rx_buf = NULL;
}
} else if (ctx->rx_buf) {
ctx->rx_buf += dfs;
}
SYS_LOG_DBG("rx buf/len %p/%u", ctx->rx_buf, ctx->rx_len);
}
static ALWAYS_INLINE
bool spi_context_rx_on(struct spi_context *ctx)
{
return !!(ctx->rx_buf || ctx->rx_len);
}
#ifdef __cplusplus
}
#endif
#endif /* __SPI_DRIVER_COMMON_H__ */