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>
This commit is contained in:
Viresh Kumar 2015-08-12 09:19:33 +05:30 committed by Greg Kroah-Hartman
parent 738599c0dd
commit 90f1b617d8
7 changed files with 282 additions and 0 deletions

View File

@ -10,6 +10,7 @@ greybus-y := core.o \
protocol.o \
control.o \
svc.o \
firmware.o \
operation.o
gb-phy-y := gpbridge.o \

View File

@ -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();

View File

@ -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);

View File

@ -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 */

View File

@ -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"

View File

@ -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,

View File

@ -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 */