[patch 27/32] greybus: bridged phy spi driver
This driver implements the Greybus bridged phy spi class protocol. Signed-off-by: Greg Kroah-Hartman--- drivers/greybus/spi.c| 79 ++ drivers/greybus/spilib.c | 565 +++ drivers/greybus/spilib.h | 24 + 3 files changed, 668 insertions(+) --- /dev/null +++ b/drivers/greybus/spi.c @@ -0,0 +1,79 @@ +/* + * SPI bridge PHY driver. + * + * Copyright 2014-2016 Google Inc. + * Copyright 2014-2016 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include + +#include "greybus.h" +#include "gbphy.h" +#include "spilib.h" + +static struct spilib_ops *spilib_ops; + +static int gb_spi_probe(struct gbphy_device *gbphy_dev, + const struct gbphy_device_id *id) +{ + struct gb_connection *connection; + int ret; + + connection = gb_connection_create(gbphy_dev->bundle, + le16_to_cpu(gbphy_dev->cport_desc->id), + NULL); + if (IS_ERR(connection)) + return PTR_ERR(connection); + + ret = gb_connection_enable(connection); + if (ret) + goto exit_connection_destroy; + + ret = gb_spilib_master_init(connection, _dev->dev, spilib_ops); + if (ret) + goto exit_connection_disable; + + gb_gbphy_set_data(gbphy_dev, connection); + + gbphy_runtime_put_autosuspend(gbphy_dev); + return 0; + +exit_connection_disable: + gb_connection_disable(connection); +exit_connection_destroy: + gb_connection_destroy(connection); + + return ret; +} + +static void gb_spi_remove(struct gbphy_device *gbphy_dev) +{ + struct gb_connection *connection = gb_gbphy_get_data(gbphy_dev); + int ret; + + ret = gbphy_runtime_get_sync(gbphy_dev); + if (ret) + gbphy_runtime_get_noresume(gbphy_dev); + + gb_spilib_master_exit(connection); + gb_connection_disable(connection); + gb_connection_destroy(connection); +} + +static const struct gbphy_device_id gb_spi_id_table[] = { + { GBPHY_PROTOCOL(GREYBUS_PROTOCOL_SPI) }, + { }, +}; +MODULE_DEVICE_TABLE(gbphy, gb_spi_id_table); + +static struct gbphy_driver spi_driver = { + .name = "spi", + .probe = gb_spi_probe, + .remove = gb_spi_remove, + .id_table = gb_spi_id_table, +}; + +module_gbphy_driver(spi_driver); +MODULE_LICENSE("GPL v2"); --- /dev/null +++ b/drivers/greybus/spilib.c @@ -0,0 +1,565 @@ +/* + * Greybus SPI library + * + * Copyright 2014-2016 Google Inc. + * Copyright 2014-2016 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include +#include +#include + +#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 intop_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
[patch 27/32] greybus: bridged phy spi driver
This driver implements the Greybus bridged phy spi class protocol. Signed-off-by: Greg Kroah-Hartman --- drivers/greybus/spi.c| 79 ++ drivers/greybus/spilib.c | 565 +++ drivers/greybus/spilib.h | 24 + 3 files changed, 668 insertions(+) --- /dev/null +++ b/drivers/greybus/spi.c @@ -0,0 +1,79 @@ +/* + * SPI bridge PHY driver. + * + * Copyright 2014-2016 Google Inc. + * Copyright 2014-2016 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include + +#include "greybus.h" +#include "gbphy.h" +#include "spilib.h" + +static struct spilib_ops *spilib_ops; + +static int gb_spi_probe(struct gbphy_device *gbphy_dev, + const struct gbphy_device_id *id) +{ + struct gb_connection *connection; + int ret; + + connection = gb_connection_create(gbphy_dev->bundle, + le16_to_cpu(gbphy_dev->cport_desc->id), + NULL); + if (IS_ERR(connection)) + return PTR_ERR(connection); + + ret = gb_connection_enable(connection); + if (ret) + goto exit_connection_destroy; + + ret = gb_spilib_master_init(connection, _dev->dev, spilib_ops); + if (ret) + goto exit_connection_disable; + + gb_gbphy_set_data(gbphy_dev, connection); + + gbphy_runtime_put_autosuspend(gbphy_dev); + return 0; + +exit_connection_disable: + gb_connection_disable(connection); +exit_connection_destroy: + gb_connection_destroy(connection); + + return ret; +} + +static void gb_spi_remove(struct gbphy_device *gbphy_dev) +{ + struct gb_connection *connection = gb_gbphy_get_data(gbphy_dev); + int ret; + + ret = gbphy_runtime_get_sync(gbphy_dev); + if (ret) + gbphy_runtime_get_noresume(gbphy_dev); + + gb_spilib_master_exit(connection); + gb_connection_disable(connection); + gb_connection_destroy(connection); +} + +static const struct gbphy_device_id gb_spi_id_table[] = { + { GBPHY_PROTOCOL(GREYBUS_PROTOCOL_SPI) }, + { }, +}; +MODULE_DEVICE_TABLE(gbphy, gb_spi_id_table); + +static struct gbphy_driver spi_driver = { + .name = "spi", + .probe = gb_spi_probe, + .remove = gb_spi_remove, + .id_table = gb_spi_id_table, +}; + +module_gbphy_driver(spi_driver); +MODULE_LICENSE("GPL v2"); --- /dev/null +++ b/drivers/greybus/spilib.c @@ -0,0 +1,565 @@ +/* + * Greybus SPI library + * + * Copyright 2014-2016 Google Inc. + * Copyright 2014-2016 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include +#include +#include +#include +#include + +#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 intop_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