[patch 27/32] greybus: bridged phy spi driver

2016-09-16 Thread Greg KH
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

2016-09-16 Thread Greg KH
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