/*
* Greybus SPI library
*
* Copyright 2014-2016 Google Inc.
* Copyright 2014-2016 Linaro Ltd.
*
* Released under the GPLv2 only.
*/
#include <linux/bitops.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include "greybus.h"
#include "spilib.h"
struct gb_spilib {
struct gb_connection *connection;
struct device *parent;
struct spi_transfer *first_xfer;
struct spi_transfer *last_xfer;
struct spilib_ops *ops;
u32 rx_xfer_offset;
u32 tx_xfer_offset;
u32 last_xfer_size;
unsigned int op_timeout;
u16 mode;
u16 flags;
u32 bits_per_word_mask;
u8 num_chipselect;
u32 min_speed_hz;
u32 max_speed_hz;
};
#define GB_SPI_STATE_MSG_DONE ((void *)0)
#define GB_SPI_STATE_MSG_IDLE ((void *)1)
#define GB_SPI_STATE_MSG_RUNNING ((void *)2)
#define GB_SPI_STATE_OP_READY ((void *)3)
#define GB_SPI_STATE_OP_DONE ((void *)4)
#define GB_SPI_STATE_MSG_ERROR ((void *)-1)
#define XFER_TIMEOUT_TOLERANCE 200
static struct spi_master *get_master_from_spi(struct gb_spilib *spi)
{
return gb_connection_get_data(spi->connection);
}
static int tx_header_fit_operation(u32 tx_size, u32 count, size_t data_max)
{
size_t headers_size;
data_max -= sizeof(struct gb_spi_transfer_request);
headers_size = (count + 1) * sizeof(struct gb_spi_transfer);
return tx_size + headers_size > data_max ? 0 : 1;
}
static size_t calc_rx_xfer_size(u32 rx_size, u32 *tx_xfer_size, u32 len,
size_t data_max)
{
size_t rx_xfer_size;
data_max -= sizeof(struct gb_spi_transfer_response);
if (rx_size + len > data_max)
rx_xfer_size = data_max - rx_size;
else
rx_xfer_size = len;
/* if this is a write_read, for symmetry read the same as write */
if (*tx_xfer_size && rx_xfer_size > *tx_xfer_size)
rx_xfer_size = *tx_xfer_size;
if (*tx_xfer_size && rx_xfer_size < *tx_xfer_size)
*tx_xfer_size = rx_xfer_size;
return rx_xfer_size;
}
static size_t calc_tx_xfer_size(u32 tx_size, u32 count, size_t len,
size_t data_max)
{
size_t headers_size;
data_max -= sizeof(struct gb_spi_transfer_request);
headers_size = (count + 1) * sizeof(struct gb_spi_transfer);
if (tx_size + headers_size + len > data_max)
return data_max - (tx_size + sizeof(struct gb_spi_transfer));
return len;
}
static void clean_xfer_state(struct gb_spilib *spi)
{
spi->first_xfer = NULL;
spi->last_xfer = NULL;
spi->rx_xfer_offset = 0;
spi->tx_xfer_offset = 0;
spi->last_xfer_size = 0;
spi->op_timeout = 0;
}
static bool is_last_xfer_done(struct gb_spilib *spi)
{
struct spi_transfer *last_xfer = spi->last_xfer;
if ((spi->tx_xfer_offset + spi->last_xfer_size == last_xfer->len) ||
(spi->rx_xfer_offset + spi->last_xfer_size == last_xfer->len))
return true;
return false;
}
static int setup_next_xfer(struct gb_spilib *spi, struct spi_message *msg)
{
struct spi_transfer *last_xfer = spi->last_xfer;
if (msg->state != GB_SPI_STATE_OP_DONE)
return 0;
/*
* if we transferred all content of the last transfer, reset values and
* check if this was the last transfer in the message
*/
if (is_last_xfer_done(spi)) {
spi->tx_xfer_offset = 0;
spi->rx_xfer_offset = 0;
spi->op_timeout = 0;
if (last_xfer == list_last_entry(&msg->transfers,
struct spi_transfer,
transfer_list))
msg->state = GB_SPI_STATE_MSG_DONE;
else
spi->first_xfer = list_next_entry(last_xfer,
transfer_list);
return 0;
}
spi->first_xfer = last_xfer;
if (last_xfer->tx_buf)
spi->tx_xfer_offset += spi->last_xfer_size;
if (last_xfer->rx_buf)
spi->rx_xfer_offset += spi->last_xfer_size;
return 0;
}
static struct spi_transfer *get_next_xfer(struct spi_transfer *xfer,
struct spi_message *msg)
{
if (xfer == list_last_entry(&msg->transfers, struct spi_transfer,
transfer_list))
return NULL;
return list_next_entry(xfer, transfer_list);
}
/* Routines to transfer data */
static struct gb_operation *gb_spi_operation_create(struct gb_spilib *spi,
struct gb_connection *connection, struct spi_message *msg)
{
struct gb_spi_transfer_request *request;
struct spi_device *dev = msg->spi;
struct spi_transfer *xfer;
struct gb_spi_transfer *gb_xfer;
struct gb_operation *operation;
u32 tx_size = 0, rx_size = 0, count = 0, xfer_len = 0, request_size;
u32 tx_xfer_size = 0, rx_xfer_size = 0, len;
u32 total_len = 0;
unsigned int xfer_timeout;
size_t data_max;
void *tx_data;
data_max = gb_operation_get_payload_size_max(connection);
xfer = spi->first_xfer;
/* Find number of transfers queued and tx/rx length in the message */
while (msg->state != GB_SPI_STATE_OP_READY) {
msg->state = GB_SPI_STATE_MSG_RUNNING;
spi->last_xfer = xfer;
if (!xfer->tx_buf && !xfer->rx_buf) {
dev_err(spi->parent,
"bufferless transfer, length %u\n", xfer->len);
msg->state = GB_SPI_STATE_MSG_ERROR;
return NULL;
}
tx_xfer_size = 0;
rx_xfer_size = 0;
if (xfer->tx_buf) {
len = xfer->len - spi->tx_xfer_offset;
if (!tx_header_fit_operation(tx_size, count, data_max))
break;
tx_xfer_size = calc_tx_xfer_size(tx_size, count,
len, data_max);
spi->last_xfer_size = tx_xfer_size;
}
if (xfer->rx_buf) {
len = xfer->len - spi->rx_xfer_offset;
rx_xfer_size = calc_rx_xfer_size(rx_size, &tx_xfer_size,
len, data_max);
spi->last_xfer_size = rx_xfer_size;
}
tx_size += tx_xfer_size;
rx_size += rx_xfer_size;
total_len += spi->last_xfer_size;
count++;
xfer = get_next_xfer(xfer, msg);
if (!xfer || total_len >= data_max)
msg->state = GB_SPI_STATE_OP_READY;
}
/*
* In addition to space for all message descriptors we need
* to have enough to hold all tx data.
*/
request_size =