From 8ebc998f5fb146b7304fb7ac4e4d80059b6197fe Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Tue, 11 Aug 2015 13:50:50 +0100 Subject: [PATCH 1/9] greybus: connection: fix jump label on device_add failure On device_add() failure in gb_connection_create_range() we jump to err_remove_ida. Instead we should be jumping to err_free_connection, so change the flow to accomodate. Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 88383b6e603f..3765aa87ef2d 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -238,7 +238,7 @@ gb_connection_create_range(struct greybus_host_device *hd, pr_err("failed to add connection device for cport 0x%04hx\n", cport_id); - goto err_remove_ida; + goto err_free_connection; } spin_lock_irq(&gb_connections_lock); From a1a4a29cb9e9593a1f47d549af212f35f131e6cc Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Tue, 11 Aug 2015 13:50:51 +0100 Subject: [PATCH 2/9] greybus: connection: add a timestamp kfifo to track connection handoff For the ES2 test activity it may be beneficial to have a performance metric that doesn't include any of the greybus stack malloc, workqueues etc. In order to faciltate, this patch adds a simple kfifo structure to hold two timestamp values. One timestamp will represent the last reasonable point a greybus outbound timestamp can be taken, the other timestamp will represent the first reasonable point an inbound timestamp can be taken. In order to facilitate this model, tracking the timestamps in the connection structure appears to be the best place to keep store of this data. Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.c | 36 +++++++++++++++++++++++++++- drivers/staging/greybus/connection.h | 5 ++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 3765aa87ef2d..0ec5b0dcc145 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -11,6 +11,10 @@ #include "greybus.h" +#define GB_CONNECTION_TS_KFIFO_ELEMENTS 2 +#define GB_CONNECTION_TS_KFIFO_LEN \ + (GB_CONNECTION_TS_KFIFO_ELEMENTS * sizeof(struct timeval)) + static DEFINE_SPINLOCK(gb_connections_lock); /* This is only used at initialization time; no locking is required. */ @@ -63,6 +67,29 @@ void greybus_data_rcvd(struct greybus_host_device *hd, u16 cport_id, } EXPORT_SYMBOL_GPL(greybus_data_rcvd); +void gb_connection_push_timestamp(struct gb_connection *connection) +{ + struct timeval tv; + + do_gettimeofday(&tv); + kfifo_in_locked(&connection->ts_kfifo, (void *)&tv, + sizeof(struct timeval), &connection->lock); +} +EXPORT_SYMBOL_GPL(gb_connection_push_timestamp); + +int gb_connection_pop_timestamp(struct gb_connection *connection, + struct timeval *tv) +{ + int retval; + + if (!kfifo_len(&connection->ts_kfifo)) + return -ENOMEM; + retval = kfifo_out_locked(&connection->ts_kfifo, (void *)tv, + sizeof(*tv), &connection->lock); + return retval; +} +EXPORT_SYMBOL_GPL(gb_connection_pop_timestamp); + static ssize_t state_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -102,6 +129,7 @@ static void gb_connection_release(struct device *dev) struct gb_connection *connection = to_gb_connection(dev); destroy_workqueue(connection->wq); + kfifo_free(&connection->ts_kfifo); kfree(connection); } @@ -222,6 +250,10 @@ gb_connection_create_range(struct greybus_host_device *hd, if (!connection->wq) goto err_free_connection; + if (kfifo_alloc(&connection->ts_kfifo, GB_CONNECTION_TS_KFIFO_LEN, + GFP_KERNEL)) + goto err_free_connection; + connection->dev.parent = parent; connection->dev.bus = &greybus_bus_type; connection->dev.type = &greybus_connection_type; @@ -238,7 +270,7 @@ gb_connection_create_range(struct greybus_host_device *hd, pr_err("failed to add connection device for cport 0x%04hx\n", cport_id); - goto err_free_connection; + goto err_free_kfifo; } spin_lock_irq(&gb_connections_lock); @@ -259,6 +291,8 @@ gb_connection_create_range(struct greybus_host_device *hd, return connection; +err_free_kfifo: + kfifo_free(&connection->ts_kfifo); err_free_connection: kfree(connection); err_remove_ida: diff --git a/drivers/staging/greybus/connection.h b/drivers/staging/greybus/connection.h index 0dbbc202e953..a26a48033fc6 100644 --- a/drivers/staging/greybus/connection.h +++ b/drivers/staging/greybus/connection.h @@ -11,6 +11,7 @@ #define __CONNECTION_H #include <linux/list.h> +#include <linux/kfifo.h> enum gb_connection_state { GB_CONNECTION_STATE_INVALID = 0, @@ -42,6 +43,7 @@ struct gb_connection { struct list_head operations; struct workqueue_struct *wq; + struct kfifo ts_kfifo; atomic_t op_cycle; @@ -65,6 +67,9 @@ void gb_hd_connections_exit(struct greybus_host_device *hd); void greybus_data_rcvd(struct greybus_host_device *hd, u16 cport_id, u8 *data, size_t length); +void gb_connection_push_timestamp(struct gb_connection *connection); +int gb_connection_pop_timestamp(struct gb_connection *connection, + struct timeval *tv); void gb_connection_bind_protocol(struct gb_connection *connection); From 3f2a809e8b4c69f61de17c3efe144b9dba23924b Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Tue, 11 Aug 2015 13:50:52 +0100 Subject: [PATCH 3/9] greybus: es-drivers: add outbound timestamp to connection In order to facilitate grabbing a timestamp that doesn't include greybus overhead, this patch adds a timestamp right before usb_submit_urb() for both es1.c and es2.c. Long term the timestmaping of messages like this probably wants to go away but, for the moment it may have some use to the firmware people instrumenting the performance of the system. Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/es1.c | 2 ++ drivers/staging/greybus/es2.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index 0cb7a3c7ef72..7fe1eaa17024 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -16,6 +16,7 @@ #include "greybus.h" #include "svc_msg.h" #include "kernel_ver.h" +#include "connection.h" /* Memory sizes for the buffers sent to/from the ES1 controller */ #define ES1_SVC_MSG_SIZE (sizeof(struct svc_msg) + SZ_64K) @@ -244,6 +245,7 @@ static int message_send(struct greybus_host_device *hd, u16 cport_id, usb_sndbulkpipe(udev, es1->cport_out_endpoint), message->buffer, buffer_size, cport_out_callback, message); + gb_connection_push_timestamp(message->operation->connection); retval = usb_submit_urb(urb, gfp_mask); if (retval) { pr_err("error %d submitting URB\n", retval); diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 323721a2e2aa..43cbc4d06e5f 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -16,6 +16,7 @@ #include "greybus.h" #include "svc_msg.h" #include "kernel_ver.h" +#include "connection.h" /* Memory sizes for the buffers sent to/from the ES1 controller */ #define ES1_SVC_MSG_SIZE (sizeof(struct svc_msg) + SZ_64K) @@ -340,6 +341,7 @@ static int message_send(struct greybus_host_device *hd, u16 cport_id, es1->cport_out[bulk_ep_set].endpoint), message->buffer, buffer_size, cport_out_callback, message); + gb_connection_push_timestamp(message->operation->connection); retval = usb_submit_urb(urb, gfp_mask); if (retval) { pr_err("error %d submitting URB\n", retval); From 4c192665f0183150cff38b6954687752f3461e13 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Tue, 11 Aug 2015 13:50:53 +0100 Subject: [PATCH 4/9] greybus: loopback: functionally decompose calculation of turn-around times We have a pattern similar to this over and over again gb->elapsed_nsecs = timeval_to_ns(&te) - timeval_to_ns(&ts); good software practice dictates we functionally decompose this. This patch decomposes into gb_loopback_calc_latency(). Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/loopback.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 88c329afd3ea..ac38644c4a48 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -219,6 +219,16 @@ static struct attribute *loopback_attrs[] = { }; ATTRIBUTE_GROUPS(loopback); +static void gb_loopback_calc_latency(struct gb_loopback *gb, + struct timeval *ts, struct timeval *te) +{ + u64 t1, t2; + + t1 = timeval_to_ns(ts); + t2 = timeval_to_ns(te); + gb->elapsed_nsecs = t2 - t1; +} + static int gb_loopback_sink(struct gb_loopback *gb, u32 len) { struct timeval ts, te; @@ -236,7 +246,7 @@ static int gb_loopback_sink(struct gb_loopback *gb, u32 len) request, len + sizeof(*request), NULL, 0); do_gettimeofday(&te); - gb->elapsed_nsecs = timeval_to_ns(&te) - timeval_to_ns(&ts); + gb_loopback_calc_latency(gb, &ts, &te); kfree(request); return retval; @@ -265,7 +275,7 @@ static int gb_loopback_transfer(struct gb_loopback *gb, u32 len) request, len + sizeof(*request), response, len + sizeof(*response)); do_gettimeofday(&te); - gb->elapsed_nsecs = timeval_to_ns(&te) - timeval_to_ns(&ts); + gb_loopback_calc_latency(gb, &ts, &te); if (retval) goto gb_error; @@ -289,7 +299,7 @@ static int gb_loopback_ping(struct gb_loopback *gb) retval = gb_operation_sync(gb->connection, GB_LOOPBACK_TYPE_PING, NULL, 0, NULL, 0); do_gettimeofday(&te); - gb->elapsed_nsecs = timeval_to_ns(&te) - timeval_to_ns(&ts); + gb_loopback_calc_latency(gb, &ts, &te); return retval; } From fd489e1ac617d662e248557afd8aa06ee731440b Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Date: Tue, 11 Aug 2015 13:50:54 +0100 Subject: [PATCH 5/9] greybus: loopback: handle timestamp roll-over This patch ensures we account for roll-over in the loopback driver. do_gettimeofday() is used to grab a timestamp. Two timestamps are derived one before and one after a gb_operation_sync(), however since do_gettimeofday() returns the number of seconds and mircoseconds that have elapsed today - we need to account for a situation where the timestamp starts at say 23:59:999us rolls over and the end time is now earlier than the start time. Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/loopback.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index ac38644c4a48..852b6beaecac 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -22,6 +22,8 @@ #include "greybus.h" +#define NSEC_PER_DAY 86400000000000ULL + struct gb_loopback_stats { u32 min; u32 max; @@ -226,7 +228,10 @@ static void gb_loopback_calc_latency(struct gb_loopback *gb, t1 = timeval_to_ns(ts); t2 = timeval_to_ns(te); - gb->elapsed_nsecs = t2 - t1; + if (t2 > t1) + gb->elapsed_nsecs = t2 - t1; + else + gb->elapsed_nsecs = NSEC_PER_DAY - t2 + t1; } static int gb_loopback_sink(struct gb_loopback *gb, u32 len) From 3944a454f1d5634cdcd8b8844199d67a1110dccb Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Wed, 12 Aug 2015 09:19:31 +0530 Subject: [PATCH 6/9] greybus: interface: Preserve data received during hotplug event This shall be used later to find a firmware blob for the interface, lets save it in the interface structure. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/interface.h | 6 ++++++ drivers/staging/greybus/svc.c | 13 +++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index e60a3705494e..38210ad4e631 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -28,6 +28,12 @@ struct gb_interface { char *product_string; u64 unique_id; + /* Information taken from the hotplug event */ + u32 unipro_mfg_id; + u32 unipro_prod_id; + u32 ara_vend_id; + u32 ara_prod_id; + struct gb_module *module; struct greybus_host_device *hd; }; diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 025b2bad9428..73e7947fadba 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -232,10 +232,6 @@ static int gb_svc_intf_hotplug_recv(struct gb_operation *op) struct device *dev = &op->connection->dev; struct gb_interface *intf; u8 intf_id, device_id; - u32 unipro_mfg_id; - u32 unipro_prod_id; - u32 ara_vend_id; - u32 ara_prod_id; int ret; if (request->payload_size < sizeof(*hotplug)) { @@ -252,10 +248,6 @@ static int gb_svc_intf_hotplug_recv(struct gb_operation *op) * XXX have the SVC get acknowledgement before we proceed. */ intf_id = hotplug->intf_id; - unipro_mfg_id = le32_to_cpu(hotplug->data.unipro_mfg_id); - unipro_prod_id = le32_to_cpu(hotplug->data.unipro_prod_id); - ara_vend_id = le32_to_cpu(hotplug->data.ara_vend_id); - ara_prod_id = le32_to_cpu(hotplug->data.ara_prod_id); intf = gb_interface_create(hd, intf_id); if (!intf) { @@ -264,6 +256,11 @@ static int gb_svc_intf_hotplug_recv(struct gb_operation *op) return -EINVAL; } + intf->unipro_mfg_id = le32_to_cpu(hotplug->data.unipro_mfg_id); + intf->unipro_prod_id = le32_to_cpu(hotplug->data.unipro_prod_id); + intf->ara_vend_id = le32_to_cpu(hotplug->data.ara_vend_id); + intf->ara_prod_id = le32_to_cpu(hotplug->data.ara_prod_id); + /* * Create a device id for the interface: * - device id 0 (GB_DEVICE_ID_SVC) belongs to the SVC From 738599c0dd7fef4d28f416ff9b0b3bc1b07468d2 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Wed, 12 Aug 2015 09:19:32 +0530 Subject: [PATCH 7/9] greybus: protocol: Create request structure from within gb_protocol_get_version() The version request can only send the version of protocol for which it is initiated and gb_protocol_get_version() has all the information to create the request structure. Replace the 'request' and 'request_size' arguments to gb_protocol_get_version() with a bool to know if the version information of the protocol should be sent or not. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.c | 2 +- drivers/staging/greybus/protocol.c | 12 ++++++++++-- drivers/staging/greybus/protocol.h | 3 +-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 0ec5b0dcc145..2b2be3fd1638 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -421,7 +421,7 @@ int gb_connection_init(struct gb_connection *connection) * this for SVC as that is initiated by the SVC. */ if (connection->hd_cport_id != GB_SVC_CPORT_ID) { - ret = gb_protocol_get_version(connection, NULL, 0); + ret = gb_protocol_get_version(connection, false); if (ret) { dev_err(&connection->dev, "Failed to get version CPort-%d (%d)\n", diff --git a/drivers/staging/greybus/protocol.c b/drivers/staging/greybus/protocol.c index b63e28c1b950..5bdc2c026efd 100644 --- a/drivers/staging/greybus/protocol.c +++ b/drivers/staging/greybus/protocol.c @@ -163,12 +163,20 @@ struct gb_protocol *gb_protocol_get(u8 id, u8 major, u8 minor) return protocol; } -int gb_protocol_get_version(struct gb_connection *connection, void *request, - int request_size) +int gb_protocol_get_version(struct gb_connection *connection, bool send_request) { struct gb_protocol_version_response response; + struct gb_protocol_version_response *request = NULL; + int request_size = 0; int retval; + if (send_request) { + response.major = connection->protocol->major; + response.minor = connection->protocol->minor; + request = &response; + request_size = sizeof(*request); + } + retval = gb_operation_sync(connection, GB_REQUEST_TYPE_PROTOCOL_VERSION, request, request_size, &response, sizeof(response)); diff --git a/drivers/staging/greybus/protocol.h b/drivers/staging/greybus/protocol.h index 34a7f185a638..87b5a1010de0 100644 --- a/drivers/staging/greybus/protocol.h +++ b/drivers/staging/greybus/protocol.h @@ -44,8 +44,7 @@ int gb_protocol_deregister(struct gb_protocol *protocol); __gb_protocol_register(protocol, THIS_MODULE) struct gb_protocol *gb_protocol_get(u8 id, u8 major, u8 minor); -int gb_protocol_get_version(struct gb_connection *connection, void *request, - int request_size); +int gb_protocol_get_version(struct gb_connection *connection, bool send_request); void gb_protocol_put(struct gb_protocol *protocol); From 90f1b617d88f145506e9061436069583cb82d101 Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Wed, 12 Aug 2015 09:19:33 +0530 Subject: [PATCH 8/9] greybus: Add firmware protocol driver This adds firmware protocol driver based on the latest specs available on mailing lists. This uses the firmware framework present in kernel. Refer Documentation/firmware_class/README on how it works. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/Makefile | 1 + drivers/staging/greybus/core.c | 9 + drivers/staging/greybus/firmware.c | 199 ++++++++++++++++++++ drivers/staging/greybus/firmware.h | 16 ++ drivers/staging/greybus/greybus.h | 1 + drivers/staging/greybus/greybus_manifest.h | 2 + drivers/staging/greybus/greybus_protocols.h | 54 ++++++ 7 files changed, 282 insertions(+) create mode 100644 drivers/staging/greybus/firmware.c create mode 100644 drivers/staging/greybus/firmware.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 1467c5b3fcd8..3c32d1427dc0 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -10,6 +10,7 @@ greybus-y := core.o \ protocol.o \ control.o \ svc.o \ + firmware.o \ operation.o gb-phy-y := gpbridge.o \ diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 225fa4fb5268..6edeec9c1fdf 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -300,8 +300,16 @@ static int __init gb_init(void) goto error_svc; } + retval = gb_firmware_protocol_init(); + if (retval) { + pr_err("gb_firmware_protocol_init failed\n"); + goto error_firmware; + } + return 0; /* Success */ +error_firmware: + gb_svc_protocol_exit(); error_svc: gb_control_protocol_exit(); error_control: @@ -321,6 +329,7 @@ module_init(gb_init); static void __exit gb_exit(void) { + gb_firmware_protocol_exit(); gb_svc_protocol_exit(); gb_control_protocol_exit(); gb_endo_exit(); diff --git a/drivers/staging/greybus/firmware.c b/drivers/staging/greybus/firmware.c new file mode 100644 index 000000000000..13efaabb891b --- /dev/null +++ b/drivers/staging/greybus/firmware.c @@ -0,0 +1,199 @@ +/* + * FIRMWARE Greybus driver. + * + * Copyright 2015 Google Inc. + * Copyright 2015 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include <linux/firmware.h> + +#include "greybus.h" + +struct gb_firmware { + struct gb_connection *connection; + const struct firmware *fw; +}; + +static void free_firmware(struct gb_firmware *firmware) +{ + release_firmware(firmware->fw); + firmware->fw = NULL; +} + +/* This returns path of the firmware blob on the disk */ +static int download_firmware(struct gb_firmware *firmware, u8 stage) +{ + struct gb_connection *connection = firmware->connection; + struct gb_interface *intf = connection->bundle->intf; + char firmware_name[28]; + + /* Already have a firmware, free it */ + if (firmware->fw) + free_firmware(firmware); + + /* + * Create firmware name + * + * XXX Name it properly.. + */ + sprintf(firmware_name, "ara:%04x:%04x:%04x:%04x:%04x.fw", intf->unipro_mfg_id, + intf->unipro_prod_id, intf->ara_vend_id, intf->ara_prod_id, + stage); + + return request_firmware(&firmware->fw, firmware_name, &connection->dev); +} + +static int gb_firmware_size_request(struct gb_operation *op) +{ + struct gb_connection *connection = op->connection; + struct gb_firmware *firmware = connection->private; + struct gb_firmware_size_request *size_request = op->request->payload; + struct gb_firmware_size_response *size_response; + struct device *dev = &connection->dev; + int ret; + + if (op->request->payload_size != sizeof(*size_request)) { + dev_err(dev, "%s: illegal size of firmware size request (%zu != %zu)\n", + __func__, op->request->payload_size, + sizeof(*size_request)); + return -EINVAL; + } + + ret = download_firmware(firmware, size_request->stage); + if (ret) { + dev_err(dev, "%s: failed to download firmware (%d)\n", __func__, + ret); + return ret; + } + + if (!gb_operation_response_alloc(op, sizeof(*size_response), + GFP_KERNEL)) { + dev_err(dev, "%s: error allocating response\n", __func__); + free_firmware(firmware); + return -ENOMEM; + } + + size_response = op->response->payload; + size_response->size = cpu_to_le32(firmware->fw->size); + + return 0; +} + +static int gb_firmware_get_firmware(struct gb_operation *op) +{ + struct gb_connection *connection = op->connection; + struct gb_firmware *firmware = connection->private; + struct gb_firmware_get_firmware_request *firmware_request = op->request->payload; + struct gb_firmware_get_firmware_response *firmware_response; + struct device *dev = &connection->dev; + unsigned int offset, size; + + if (op->request->payload_size != sizeof(*firmware_request)) { + dev_err(dev, "%s: Illegal size of get firmware request (%zu %zu)\n", + __func__, op->request->payload_size, + sizeof(*firmware_request)); + return -EINVAL; + } + + if (!firmware->fw) { + dev_err(dev, "%s: firmware not available\n", __func__); + return -EINVAL; + } + + offset = le32_to_cpu(firmware_request->offset); + size = le32_to_cpu(firmware_request->size); + + if (!gb_operation_response_alloc(op, sizeof(*firmware_response) + size, + GFP_KERNEL)) { + dev_err(dev, "%s: error allocating response\n", __func__); + return -ENOMEM; + } + + firmware_response = op->response->payload; + memcpy(firmware_response->data, firmware->fw->data + offset, size); + + return 0; +} + +static int gb_firmware_ready_to_boot(struct gb_operation *op) +{ + struct gb_connection *connection = op->connection; + struct gb_firmware_ready_to_boot_request *rtb_request = op->request->payload; + struct device *dev = &connection->dev; + u8 stage, status; + + if (op->request->payload_size != sizeof(*rtb_request)) { + dev_err(dev, "%s: Illegal size of ready to boot request (%zu %zu)\n", + __func__, op->request->payload_size, + sizeof(*rtb_request)); + return -EINVAL; + } + + stage = rtb_request->stage; + status = rtb_request->status; + + /* Return error if the blob was invalid */ + if (status == GB_FIRMWARE_BOOT_STATUS_INVALID) + return -EINVAL; + + /* + * XXX Should we return error for insecure firmware? + */ + + return 0; +} + +static int gb_firmware_request_recv(u8 type, struct gb_operation *op) +{ + switch (type) { + case GB_FIRMWARE_TYPE_FIRMWARE_SIZE: + return gb_firmware_size_request(op); + case GB_FIRMWARE_TYPE_GET_FIRMWARE: + return gb_firmware_get_firmware(op); + case GB_FIRMWARE_TYPE_READY_TO_BOOT: + return gb_firmware_ready_to_boot(op); + default: + dev_err(&op->connection->dev, + "unsupported request: %hhu\n", type); + return -EINVAL; + } +} + +static int gb_firmware_connection_init(struct gb_connection *connection) +{ + struct gb_firmware *firmware; + + firmware = kzalloc(sizeof(*firmware), GFP_KERNEL); + if (!firmware) + return -ENOMEM; + + firmware->connection = connection; + connection->private = firmware; + + return 0; +} + +static void gb_firmware_connection_exit(struct gb_connection *connection) +{ + struct gb_firmware *firmware = connection->private; + + /* Release firmware */ + if (firmware->fw) + free_firmware(firmware); + + connection->private = NULL; + kfree(firmware); +} + +static struct gb_protocol firmware_protocol = { + .name = "firmware", + .id = GREYBUS_PROTOCOL_FIRMWARE, + .major = GB_FIRMWARE_VERSION_MAJOR, + .minor = GB_FIRMWARE_VERSION_MINOR, + .connection_init = gb_firmware_connection_init, + .connection_exit = gb_firmware_connection_exit, + .request_recv = gb_firmware_request_recv, +}; +gb_builtin_protocol_driver(firmware_protocol); diff --git a/drivers/staging/greybus/firmware.h b/drivers/staging/greybus/firmware.h new file mode 100644 index 000000000000..548d297eec63 --- /dev/null +++ b/drivers/staging/greybus/firmware.h @@ -0,0 +1,16 @@ +/* + * Greybus firmware code + * + * Copyright 2015 Google Inc. + * Copyright 2015 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#ifndef __FIRMWARE_H +#define __FIRMWARE_H + +int gb_firmware_protocol_init(void); +void gb_firmware_protocol_exit(void); + +#endif /* __FIRMWARE_H */ diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index 2214f447df2b..0d4ca700a711 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -27,6 +27,7 @@ #include "manifest.h" #include "endo.h" #include "svc.h" +#include "firmware.h" #include "module.h" #include "control.h" #include "interface.h" diff --git a/drivers/staging/greybus/greybus_manifest.h b/drivers/staging/greybus/greybus_manifest.h index 9c4d7cae9bbb..687adf2cb645 100644 --- a/drivers/staging/greybus/greybus_manifest.h +++ b/drivers/staging/greybus/greybus_manifest.h @@ -43,6 +43,7 @@ enum greybus_protocol { GREYBUS_PROTOCOL_I2S_RECEIVER = 0x12, GREYBUS_PROTOCOL_I2S_TRANSMITTER = 0x13, GREYBUS_PROTOCOL_SVC = 0x14, + GREYBUS_PROTOCOL_FIRMWARE = 0x15, /* ... */ GREYBUS_PROTOCOL_RAW = 0xfe, GREYBUS_PROTOCOL_VENDOR = 0xff, @@ -70,6 +71,7 @@ enum greybus_class_type { GREYBUS_CLASS_I2S_RECEIVER = 0x12, GREYBUS_CLASS_I2S_TRANSMITTER = 0x13, GREYBUS_CLASS_SVC = 0x14, + GREYBUS_CLASS_FIRMWARE = 0x15, /* ... */ GREYBUS_CLASS_RAW = 0xfe, GREYBUS_CLASS_VENDOR = 0xff, diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index b95d24bd8e62..357ecd371adb 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -146,6 +146,60 @@ struct gb_control_disconnected_request { }; /* Control protocol [dis]connected response has no payload */ + +/* Firmware Protocol */ + +/* Version of the Greybus firmware protocol we support */ +#define GB_FIRMWARE_VERSION_MAJOR 0x00 +#define GB_FIRMWARE_VERSION_MINOR 0x01 + +/* Greybus firmware request types */ +#define GB_FIRMWARE_TYPE_INVALID 0x00 +#define GB_FIRMWARE_TYPE_PROTOCOL_VERSION 0x01 +#define GB_FIRMWARE_TYPE_FIRMWARE_SIZE 0x02 +#define GB_FIRMWARE_TYPE_GET_FIRMWARE 0x03 +#define GB_FIRMWARE_TYPE_READY_TO_BOOT 0x04 + +/* Greybus firmware boot stages */ +#define GB_FIRMWARE_BOOT_STAGE_ONE 0x01 /* Reserved for the boot ROM */ +#define GB_FIRMWARE_BOOT_STAGE_TWO 0x02 /* Firmware package to be loaded by the boot ROM */ +#define GB_FIRMWARE_BOOT_STAGE_THREE 0x03 /* Module personality package loaded by Stage 2 firmware */ + +/* Greybus firmware ready to boot status */ +#define GB_FIRMWARE_BOOT_STATUS_INVALID 0x00 /* Firmware blob could not be validated */ +#define GB_FIRMWARE_BOOT_STATUS_INSECURE 0x01 /* Firmware blob is valid but insecure */ +#define GB_FIRMWARE_BOOT_STATUS_SECURE 0x02 /* Firmware blob is valid and secure */ + +/* Max firmware data fetch size in bytes */ +#define GB_FIRMWARE_FETCH_MAX 2000 + +/* Firmware protocol firmware size request/response */ +struct gb_firmware_size_request { + __u8 stage; +}; + +struct gb_firmware_size_response { + __le32 size; +}; + +/* Firmware protocol get firmware request/response */ +struct gb_firmware_get_firmware_request { + __le32 offset; + __le32 size; +}; + +struct gb_firmware_get_firmware_response { + __u8 data[0]; +}; + +/* Firmware protocol Ready to boot request */ +struct gb_firmware_ready_to_boot_request { + __u8 stage; + __u8 status; +}; +/* Firmware protocol Ready to boot response has no payload */ + + /* I2C */ /* Version of the Greybus i2c protocol we support */ From 58dab0f2a872be5dc2bdb15f3dc487b4a1b41aaf Mon Sep 17 00:00:00 2001 From: Viresh Kumar <viresh.kumar@linaro.org> Date: Wed, 12 Aug 2015 09:19:34 +0530 Subject: [PATCH 9/9] greybus: connection: Send protocol version for firmware protocol As per greybus specs, we need to send the protocol version for firmware protocol and so this special case Hack. Probably we should always send the protocol version AP supports and kill this hack completely. But then it requires updates to specs as well, and that should be done after some discussion. For now, add a FIXME for that and a special case for firmware protocol. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> --- drivers/staging/greybus/connection.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 2b2be3fd1638..6078443a7dca 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -421,7 +421,19 @@ int gb_connection_init(struct gb_connection *connection) * this for SVC as that is initiated by the SVC. */ if (connection->hd_cport_id != GB_SVC_CPORT_ID) { - ret = gb_protocol_get_version(connection, false); + bool send_request = false; + + /* + * We need to send the protocol version of the firmware protocol + * supported by AP and so this special case. + */ + if (connection->protocol->id == GREYBUS_PROTOCOL_FIRMWARE) + send_request = true; + + // FIXME: Should we always send the protocol version AP can + // support ? + + ret = gb_protocol_get_version(connection, send_request); if (ret) { dev_err(&connection->dev, "Failed to get version CPort-%d (%d)\n",