]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
greybus: add i2c driver
authorAlex Elder <elder@linaro.org>
Thu, 16 Oct 2014 11:35:38 +0000 (06:35 -0500)
committerGreg Kroah-Hartman <greg@kroah.com>
Fri, 17 Oct 2014 16:14:11 +0000 (18:14 +0200)
This patch adds the i2c driver, based on the use of Greybus operations
over Greybus connections.  It basically replaces almost all of what
was previously found in "i2c-gb.c".

When gb_connection_device_init(connection) is called, any connection
that talks the GREYBUS_PROTOCOL_I2C is passed to gb_i2c_device_init()
to be initialized.

Initialization involves verifying the code is able to support the
version of the protocol.  For I2C, we then query the functionality
mask, and set the retry count and timeout to default values.

After that, we set up the i2c device and associate it with the
connection.  The i2c_algorithm methods are then implemented
by translating them into Greybus operations.

Signed-off-by: Alex Elder <elder@linaro.org>
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
drivers/staging/greybus/connection.c
drivers/staging/greybus/i2c-gb.c
drivers/staging/greybus/operation.c

index 0143a4f6bd1263d9250ca9f1243359172655d3ef..fc692f0ad2eb74d1f780b654b269e6edb9c1e236 100644 (file)
@@ -202,6 +202,7 @@ int gb_connection_init(struct gb_connection *connection)
 {
        switch (connection->protocol) {
        case GREYBUS_PROTOCOL_I2C:
+               return gb_i2c_device_init(connection);
        case GREYBUS_PROTOCOL_CONTROL:
        case GREYBUS_PROTOCOL_AP:
        case GREYBUS_PROTOCOL_GPIO:
index 0da195898ad43dd2ee510248054937138941277b..c4e47effa9f54010b49475944131f6c66e0a6cf8 100644 (file)
 #include "greybus.h"
 
 struct gb_i2c_device {
-       struct i2c_adapter *adapter;
-       struct gb_module *gmod;
+       struct gb_connection    *connection;
+       u8                      version_major;
+       u8                      version_minor;
+
+       u32                     functionality;
+       u16                     timeout_msec;
+       u8                      retries;
+
+       struct i2c_adapter      *adapter;
+};
+
+/* Version of the Greybus i2c protocol we support */
+#define        GB_I2C_VERSION_MAJOR            0x00
+#define        GB_I2C_VERSION_MINOR            0x01
+
+/* Greybus i2c request types */
+#define        GB_I2C_TYPE_INVALID             0x00
+#define        GB_I2C_TYPE_PROTOCOL_VERSION    0x01
+#define        GB_I2C_TYPE_FUNCTIONALITY       0x02
+#define        GB_I2C_TYPE_TIMEOUT             0x03
+#define        GB_I2C_TYPE_RETRIES             0x04
+#define        GB_I2C_TYPE_TRANSFER            0x05
+#define        GB_I2C_TYPE_RESPONSE            0x80    /* OR'd with rest */
+
+#define        GB_I2C_RETRIES_DEFAULT          3
+#define        GB_I2C_TIMEOUT_DEFAULT          1000    /* milliseconds */
+
+/* version request has no payload */
+struct gb_i2c_proto_version_response {
+       __u8    status;
+       __u8    major;
+       __u8    minor;
+};
+
+/* functionality request has no payload */
+struct gb_i2c_functionality_response {
+       __u8    status;
+       __le32  functionality;
 };
 
-static const struct greybus_module_id id_table[] = {
-       { GREYBUS_DEVICE(0x42, 0x42) }, /* make shit up */
-       { },    /* terminating NULL entry */
+struct gb_i2c_timeout_request {
+       __le16  msec;
+};
+struct gb_i2c_timeout_response {
+       __u8    status;
+};
+
+struct gb_i2c_retries_request {
+       __u8    retries;
+};
+struct gb_i2c_retries_response {
+       __u8    status;
+};
+
+/*
+ * Outgoing data immediately follows the op count and ops array.
+ * The data for each write (master -> slave) op in the array is sent
+ * in order, with no (e.g. pad) bytes separating them.
+ *
+ * Short reads cause the entire transfer request to fail So response
+ * payload consists only of bytes read, and the number of bytes is
+ * exactly what was specified in the corresponding op.  Like
+ * outgoing data, the incoming data is in order and contiguous.
+ */
+struct gb_i2c_transfer_op {
+       __le16  addr;
+       __le16  flags;
+       __le16  size;
 };
 
-/* We BETTER be able to do SMBUS protocl calls, otherwise we are bit-banging the
- * slowest thing possible over the fastest bus possible, crazy...
- * FIXME - research this, for now just assume we can
+struct gb_i2c_transfer_request {
+       __le16                          op_count;
+       struct gb_i2c_transfer_op       ops[0];         /* op_count of these */
+};
+struct gb_i2c_transfer_response {
+       __u8                            status;
+       __u8                            data[0];        /* inbound data */
+};
+
+/*
+ * This request only uses the connection field, and if successful,
+ * fills in the major and minor protocol version of the target.
+ */
+static int gb_i2c_proto_version_operation(struct gb_i2c_device *gb_i2c_dev)
+{
+       struct gb_connection *connection = gb_i2c_dev->connection;
+       struct gb_operation *operation;
+       struct gb_i2c_proto_version_response *response;
+       int ret;
+
+       /* A protocol version request has no payload */
+       operation = gb_operation_create(connection,
+                                       GB_I2C_TYPE_PROTOCOL_VERSION,
+                                       0, sizeof(*response));
+       if (!operation)
+               return -ENOMEM;
+
+       /* Synchronous operation--no callback */
+       ret = gb_operation_request_send(operation, NULL);
+       if (ret) {
+               pr_err("version operation failed (%d)\n", ret);
+               goto out;
+       }
+
+       response = operation->response_payload;
+       if (response->status) {
+               gb_connection_err(connection, "version response %hhu",
+                       response->status);
+               ret = -EIO;
+       } else {
+               if (response->major > GB_I2C_VERSION_MAJOR) {
+                       pr_err("unsupported major version (%hhu > %hhu)\n",
+                               response->major, GB_I2C_VERSION_MAJOR);
+                       ret = -ENOTSUPP;
+                       goto out;
+               }
+               gb_i2c_dev->version_major = response->major;
+               gb_i2c_dev->version_minor = response->minor;
+       }
+out:
+
+       gb_operation_destroy(operation);
+
+       return ret;
+}
+
+/*
+ * Map Greybus i2c functionality bits into Linux ones
  */
+static u32 gb_i2c_functionality_map(u32 gb_i2c_functionality)
+{
+       return gb_i2c_functionality;    /* All bits the same for now */
+}
+
+static int gb_i2c_functionality_operation(struct gb_i2c_device *gb_i2c_dev)
+{
+       struct gb_connection *connection = gb_i2c_dev->connection;
+       struct gb_operation *operation;
+       struct gb_i2c_functionality_response *response;
+       u32 functionality;
+       int ret;
+
+       /* A functionality request has no payload */
+       operation = gb_operation_create(connection,
+                                       GB_I2C_TYPE_FUNCTIONALITY,
+                                       0, sizeof(*response));
+       if (!operation)
+               return -ENOMEM;
+
+       /* Synchronous operation--no callback */
+       ret = gb_operation_request_send(operation, NULL);
+       if (ret) {
+               pr_err("functionality operation failed (%d)\n", ret);
+               goto out;
+       }
+
+       response = operation->response_payload;
+       if (response->status) {
+               gb_connection_err(connection, "functionality response %hhu",
+                       response->status);
+               ret = -EIO;
+       } else {
+               functionality = le32_to_cpu(response->functionality);
+               gb_i2c_dev->functionality =
+                       gb_i2c_functionality_map(functionality);
+       }
+out:
+       gb_operation_destroy(operation);
 
+       return ret;
+}
 
-static s32 i2c_gb_access(struct i2c_adapter *adap, u16 addr,
-                        unsigned short flags, char read_write, u8 command,
-                        int size, union i2c_smbus_data *data)
+static int gb_i2c_timeout_operation(struct gb_i2c_device *gb_i2c_dev, u16 msec)
+{
+       struct gb_connection *connection = gb_i2c_dev->connection;
+       struct gb_operation *operation;
+       struct gb_i2c_timeout_request *request;
+       struct gb_i2c_timeout_response *response;
+       int ret;
+
+       operation = gb_operation_create(connection, GB_I2C_TYPE_TIMEOUT,
+                                       sizeof(*request), sizeof(*response));
+       if (!operation)
+               return -ENOMEM;
+       request = operation->request_payload;
+       request->msec = cpu_to_le16(msec);
+
+       /* Synchronous operation--no callback */
+       ret = gb_operation_request_send(operation, NULL);
+       if (ret) {
+               pr_err("timeout operation failed (%d)\n", ret);
+               goto out;
+       }
+
+       response = operation->response_payload;
+       if (response->status) {
+               gb_connection_err(connection, "timeout response %hhu",
+                       response->status);
+               ret = -EIO;
+       } else {
+               gb_i2c_dev->timeout_msec = msec;
+       }
+out:
+       gb_operation_destroy(operation);
+
+       return ret;
+}
+
+static int gb_i2c_retries_operation(struct gb_i2c_device *gb_i2c_dev,
+                               u8 retries)
+{
+       struct gb_connection *connection = gb_i2c_dev->connection;
+       struct gb_operation *operation;
+       struct gb_i2c_retries_request *request;
+       struct gb_i2c_retries_response *response;
+       int ret;
+
+       operation = gb_operation_create(connection, GB_I2C_TYPE_RETRIES,
+                                       sizeof(*request), sizeof(*response));
+       if (!operation)
+               return -ENOMEM;
+       request = operation->request_payload;
+       request->retries = retries;
+
+       /* Synchronous operation--no callback */
+       ret = gb_operation_request_send(operation, NULL);
+       if (ret) {
+               pr_err("retries operation failed (%d)\n", ret);
+               goto out;
+       }
+
+       response = operation->response_payload;
+       if (response->status) {
+               gb_connection_err(connection, "retries response %hhu",
+                       response->status);
+               ret = -EIO;
+       } else {
+               gb_i2c_dev->retries = retries;
+       }
+out:
+       gb_operation_destroy(operation);
+
+       return ret;
+}
+
+
+/*
+ * Map Linux i2c_msg flags into Greybus i2c transfer op flags.
+ */
+static u16 gb_i2c_transfer_op_flags_map(u16 flags)
+{
+       return flags;   /* All flags the same for now */
+}
+
+static void
+gb_i2c_fill_transfer_op(struct gb_i2c_transfer_op *op, struct i2c_msg *msg)
+{
+       u16 flags = gb_i2c_transfer_op_flags_map(msg->flags);
+
+       op->addr = cpu_to_le16(msg->addr);
+       op->flags = cpu_to_le16(flags);
+       op->size = cpu_to_le16(msg->len);
+}
+
+static struct gb_operation *
+gb_i2c_transfer_request(struct gb_connection *connection,
+                               struct i2c_msg *msgs, u32 msg_count)
+{
+       struct gb_i2c_transfer_request *request;
+       struct gb_operation *operation;
+       struct gb_i2c_transfer_op *op;
+       struct i2c_msg *msg;
+       u32 data_out_size = 0;
+       u32 data_in_size = 1;   /* Response begins with a status byte */
+       size_t request_size;
+       void *data;
+       u16 op_count;
+       u32 i;
+
+       if (msg_count > (u32)U16_MAX) {
+               gb_connection_err(connection, "msg_count (%u) too big",
+                                       msg_count);
+               return NULL;
+       }
+       op_count = (u16)msg_count;
+
+       /*
+        * In addition to space for all message descriptors we need
+        * to have enough to hold all outbound message data.
+        */
+       msg = msgs;
+       for (i = 0; i < msg_count; i++, msg++)
+               if (msg->flags & I2C_M_RD)
+                       data_in_size += (u32)msg->len;
+               else
+                       data_out_size += (u32)msg->len;
+
+       request_size = sizeof(struct gb_i2c_transfer_request);
+       request_size += msg_count * sizeof(struct gb_i2c_transfer_op);
+       request_size += data_out_size;
+
+       /* Response consists only of incoming data */
+       operation = gb_operation_create(connection, GB_I2C_TYPE_TRANSFER,
+                               request_size, data_in_size);
+       if (!operation)
+               return NULL;
+
+       request = operation->request_payload;
+       request->op_count = cpu_to_le16(op_count);
+       /* Fill in the ops array */
+       op = &request->ops[0];
+       msg = msgs;
+       for (i = 0; i < msg_count; i++)
+               gb_i2c_fill_transfer_op(op++, msg++);
+
+       if (!data_out_size)
+               return operation;
+
+       /* Copy over the outgoing data; it starts after the last op */
+       data = op;
+       msg = msgs;
+       for (i = 0; i < msg_count; i++) {
+               if (!(msg->flags & I2C_M_RD)) {
+                       memcpy(data, msg->buf, msg->len);
+                       data += msg->len;
+               }
+               msg++;
+       }
+
+       return operation;
+}
+
+static void gb_i2c_transfer_response(struct i2c_msg *msgs, u32 msg_count,
+                                       void *data)
+{
+       struct i2c_msg *msg = msgs;
+       u32 i;
+
+       for (i = 0; i < msg_count; i++) {
+               if (msg->flags & I2C_M_RD) {
+                       memcpy(msg->buf, data, msg->len);
+                       data += msg->len;
+               }
+               msg++;
+       }
+}
+
+static int gb_i2c_transfer_operation(struct gb_i2c_device *gb_i2c_dev,
+                                       struct i2c_msg *msgs, u32 msg_count)
+{
+       struct gb_connection *connection = gb_i2c_dev->connection;
+       struct gb_i2c_transfer_response *response;
+       struct gb_operation *operation;
+       int ret;
+
+       operation = gb_i2c_transfer_request(connection, msgs, msg_count);
+       if (!operation)
+               return -ENOMEM;
+
+       /* Synchronous operation--no callback */
+       ret = gb_operation_request_send(operation, NULL);
+       if (ret) {
+               pr_err("transfer operation failed (%d)\n", ret);
+               goto out;
+       }
+
+       response = operation->response_payload;
+       if (response->status) {
+               gb_connection_err(connection, "transfer response %hhu",
+                       response->status);
+               ret = -EIO;
+       } else {
+               gb_i2c_transfer_response(msgs, msg_count, response->data);
+               ret = msg_count;
+       }
+out:
+       gb_operation_destroy(operation);
+
+       return ret;
+}
+
+static int gb_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+               int msg_count)
+{
+       struct gb_i2c_device *gb_i2c_dev;
+
+       gb_i2c_dev = i2c_get_adapdata(adap);
+
+       return gb_i2c_transfer_operation(gb_i2c_dev, msgs, msg_count);
+}
+
+#if 0
+/* Later */
+static int gb_i2c_smbus_xfer(struct i2c_adapter *adap,
+                       u16 addr, unsigned short flags, char read_write,
+                       u8 command, int size, union i2c_smbus_data *data)
 {
        struct gb_i2c_device *gb_i2c_dev;
-       struct gb_module *gmod;
 
        gb_i2c_dev = i2c_get_adapdata(adap);
-       gmod = gb_i2c_dev->gmod;
-
-       // FIXME - do the actual work of sending a i2c message here...
-       switch (size) {
-       case I2C_SMBUS_QUICK:
-       case I2C_SMBUS_BYTE:
-       case I2C_SMBUS_BYTE_DATA:
-       case I2C_SMBUS_WORD_DATA:
-       case I2C_SMBUS_PROC_CALL:
-       case I2C_SMBUS_BLOCK_DATA:
-       case I2C_SMBUS_I2C_BLOCK_BROKEN:
-       case I2C_SMBUS_BLOCK_PROC_CALL:
-       case I2C_SMBUS_I2C_BLOCK_DATA:
-       default:
-               dev_err(&gmod->dev, "Unsupported transaction %d\n", size);
-               return -EOPNOTSUPP;
-       }
 
        return 0;
 }
+#endif
 
-static u32 i2c_gb_func(struct i2c_adapter *adapter)
+static u32 gb_i2c_functionality(struct i2c_adapter *adap)
 {
-       // FIXME - someone figure out what we really can support, for now just guess...
-       return I2C_FUNC_SMBUS_QUICK |
-               I2C_FUNC_SMBUS_BYTE |
-               I2C_FUNC_SMBUS_BYTE_DATA |
-               I2C_FUNC_SMBUS_WORD_DATA |
-               I2C_FUNC_SMBUS_BLOCK_DATA |
-               I2C_FUNC_SMBUS_WRITE_I2C_BLOCK |
-               I2C_FUNC_SMBUS_PEC |
-               I2C_FUNC_SMBUS_READ_I2C_BLOCK;
+       struct gb_i2c_device *gb_i2c_dev = i2c_get_adapdata(adap);
+
+       return gb_i2c_dev->functionality;
 }
 
-static const struct i2c_algorithm smbus_algorithm = {
-       .smbus_xfer     = i2c_gb_access,
-       .functionality  = i2c_gb_func,
+static const struct i2c_algorithm gb_i2c_algorithm = {
+       .master_xfer    = gb_i2c_master_xfer,
+       /* .smbus_xfer  = gb_i2c_smbus_xfer, */
+       .functionality  = gb_i2c_functionality,
 };
 
-int gb_i2c_probe(struct gb_module *gmod,
-                const struct greybus_module_id *id)
+/*
+ * Do initial setup of the i2c device.  This includes verifying we
+ * can support it (based on the protocol version it advertises).
+ * If that's OK, we get and cached its functionality bits, and
+ * set up the retry count and timeout.
+ *
+ * Note: gb_i2c_dev->connection is assumed to have been valid.
+ */
+static int gb_i2c_device_setup(struct gb_i2c_device *gb_i2c_dev)
+{
+       int ret;
+
+       /* First thing we need to do is check the version */
+       ret = gb_i2c_proto_version_operation(gb_i2c_dev);
+       if (ret)
+               return ret;
+
+       /* Assume the functionality never changes, just get it once */
+       ret = gb_i2c_functionality_operation(gb_i2c_dev);
+       if (ret)
+               return ret;
+
+       /* Set up our default retry count and timeout */
+       ret = gb_i2c_retries_operation(gb_i2c_dev, GB_I2C_RETRIES_DEFAULT);
+       if (ret)
+               return ret;
+
+       return gb_i2c_timeout_operation(gb_i2c_dev, GB_I2C_TIMEOUT_DEFAULT);
+}
+
+int gb_i2c_device_init(struct gb_connection *connection)
 {
        struct gb_i2c_device *gb_i2c_dev;
-       struct i2c_adapter *adapter;
-       int retval;
+       struct i2c_adapter *adapter = NULL;
+       int ret;
 
        gb_i2c_dev = kzalloc(sizeof(*gb_i2c_dev), GFP_KERNEL);
        if (!gb_i2c_dev)
                return -ENOMEM;
+
+       gb_i2c_dev->connection = connection;    /* refcount? */
+
+       ret = gb_i2c_device_setup(gb_i2c_dev);
+       if (ret)
+               goto out_err;
+
+       /* Looks good; allocate and set up our i2c adapter */
        adapter = kzalloc(sizeof(*adapter), GFP_KERNEL);
        if (!adapter) {
-               kfree(gb_i2c_dev);
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto out_err;
        }
 
-       i2c_set_adapdata(adapter, gb_i2c_dev);
        adapter->owner = THIS_MODULE;
        adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
-       adapter->algo = &smbus_algorithm;
-       adapter->dev.parent = &gmod->dev;
-       adapter->retries = 3;   /* we have to pick something... */
+       adapter->algo = &gb_i2c_algorithm;
+       /* adapter->algo_data = what? */
+       adapter->timeout = gb_i2c_dev->timeout_msec * HZ / 1000;
+       adapter->retries = gb_i2c_dev->retries;
+
+       /* XXX I think this parent device is wrong, but it uses existing code */
+       adapter->dev.parent = &connection->interface->gmod->dev;
        snprintf(adapter->name, sizeof(adapter->name), "Greybus i2c adapter");
-       retval = i2c_add_adapter(adapter);
-       if (retval) {
-               dev_err(&gmod->dev, "Can not add SMBus adapter\n");
-               goto error;
-       }
+       i2c_set_adapdata(adapter, gb_i2c_dev);
+
+       ret = i2c_add_adapter(adapter);
+       if (ret)
+               goto out_err;
 
-       gb_i2c_dev->gmod = gmod;
        gb_i2c_dev->adapter = adapter;
+       connection->private = gb_i2c_dev;
 
-       gmod->gb_i2c_dev = gb_i2c_dev;
        return 0;
-error:
+out_err:
        kfree(adapter);
+       /* kref_put(gb_i2c_dev->connection) */
        kfree(gb_i2c_dev);
-       return retval;
+
+       return ret;
 }
 
-void gb_i2c_disconnect(struct gb_module *gmod)
+void gb_i2c_device_exit(struct gb_connection *connection)
 {
-       struct gb_i2c_device *gb_i2c_dev;
+       struct gb_i2c_device *gb_i2c_dev = connection->private;
 
-       gb_i2c_dev = gmod->gb_i2c_dev;
-       if (!gb_i2c_dev)
-               return;
        i2c_del_adapter(gb_i2c_dev->adapter);
        kfree(gb_i2c_dev->adapter);
+       /* kref_put(gb_i2c_dev->connection) */
        kfree(gb_i2c_dev);
 }
 
 #if 0
-static struct greybus_driver i2c_gb_driver = {
-       .probe =        gb_i2c_probe,
-       .disconnect =   gb_i2c_disconnect,
-       .id_table =     id_table,
-};
-
 module_greybus_driver(i2c_gb_driver);
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>");
index 7753bf748e2c1edc222528365a61cc74b52b52e6..a49d92957c6b1c28012829b08bb1f016cd64000a 100644 (file)
@@ -148,13 +148,21 @@ int gb_operation_wait(struct gb_operation *operation)
 
 }
 
+/*
+ * This handler is used if no operation response messages are ever
+ * expected for a given protocol.
+ */
+static void gb_operation_recv_none(struct gb_operation *operation)
+{
+       /* Nothing to do! */
+}
 
 typedef void (*gb_operation_recv_handler)(struct gb_operation *operation);
 static gb_operation_recv_handler gb_operation_recv_handlers[] = {
        [GREYBUS_PROTOCOL_CONTROL]      = NULL,
        [GREYBUS_PROTOCOL_AP]           = NULL,
        [GREYBUS_PROTOCOL_GPIO]         = NULL,
-       [GREYBUS_PROTOCOL_I2C]          = NULL,
+       [GREYBUS_PROTOCOL_I2C]          = gb_operation_recv_none,
        [GREYBUS_PROTOCOL_UART]         = NULL,
        [GREYBUS_PROTOCOL_HID]          = NULL,
        [GREYBUS_PROTOCOL_VENDOR]       = NULL,