summaryrefslogtreecommitdiff
path: root/drivers/fpga/fpga-mgr.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/fpga/fpga-mgr.c')
-rw-r--r--drivers/fpga/fpga-mgr.c335
1 files changed, 264 insertions, 71 deletions
diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
index d49a9ce34568..0f4035b089a2 100644
--- a/drivers/fpga/fpga-mgr.c
+++ b/drivers/fpga/fpga-mgr.c
@@ -19,7 +19,7 @@
#include <linux/highmem.h>
static DEFINE_IDA(fpga_mgr_ida);
-static struct class *fpga_mgr_class;
+static const struct class fpga_mgr_class;
struct fpga_mgr_devres {
struct fpga_manager *mgr;
@@ -74,6 +74,15 @@ static inline int fpga_mgr_write_complete(struct fpga_manager *mgr,
return 0;
}
+static inline int fpga_mgr_parse_header(struct fpga_manager *mgr,
+ struct fpga_image_info *info,
+ const char *buf, size_t count)
+{
+ if (mgr->mops->parse_header)
+ return mgr->mops->parse_header(mgr, info, buf, count);
+ return 0;
+}
+
static inline int fpga_mgr_write_init(struct fpga_manager *mgr,
struct fpga_image_info *info,
const char *buf, size_t count)
@@ -136,23 +145,141 @@ void fpga_image_info_free(struct fpga_image_info *info)
EXPORT_SYMBOL_GPL(fpga_image_info_free);
/*
- * Call the low level driver's write_init function. This will do the
+ * Call the low level driver's parse_header function with entire FPGA image
+ * buffer on the input. This will set info->header_size and info->data_size.
+ */
+static int fpga_mgr_parse_header_mapped(struct fpga_manager *mgr,
+ struct fpga_image_info *info,
+ const char *buf, size_t count)
+{
+ int ret;
+
+ mgr->state = FPGA_MGR_STATE_PARSE_HEADER;
+ ret = fpga_mgr_parse_header(mgr, info, buf, count);
+
+ if (info->header_size + info->data_size > count) {
+ dev_err(&mgr->dev, "Bitstream data outruns FPGA image\n");
+ ret = -EINVAL;
+ }
+
+ if (ret) {
+ dev_err(&mgr->dev, "Error while parsing FPGA image header\n");
+ mgr->state = FPGA_MGR_STATE_PARSE_HEADER_ERR;
+ }
+
+ return ret;
+}
+
+/*
+ * Call the low level driver's parse_header function with first fragment of
+ * scattered FPGA image on the input. If header fits first fragment,
+ * parse_header will set info->header_size and info->data_size. If it is not,
+ * parse_header will set desired size to info->header_size and -EAGAIN will be
+ * returned.
+ */
+static int fpga_mgr_parse_header_sg_first(struct fpga_manager *mgr,
+ struct fpga_image_info *info,
+ struct sg_table *sgt)
+{
+ struct sg_mapping_iter miter;
+ int ret;
+
+ mgr->state = FPGA_MGR_STATE_PARSE_HEADER;
+
+ sg_miter_start(&miter, sgt->sgl, sgt->nents, SG_MITER_FROM_SG);
+ if (sg_miter_next(&miter) &&
+ miter.length >= info->header_size)
+ ret = fpga_mgr_parse_header(mgr, info, miter.addr, miter.length);
+ else
+ ret = -EAGAIN;
+ sg_miter_stop(&miter);
+
+ if (ret && ret != -EAGAIN) {
+ dev_err(&mgr->dev, "Error while parsing FPGA image header\n");
+ mgr->state = FPGA_MGR_STATE_PARSE_HEADER_ERR;
+ }
+
+ return ret;
+}
+
+/*
+ * Copy scattered FPGA image fragments to temporary buffer and call the
+ * low level driver's parse_header function. This should be called after
+ * fpga_mgr_parse_header_sg_first() returned -EAGAIN. In case of success,
+ * pointer to the newly allocated image header copy will be returned and
+ * its size will be set into *ret_size. Returned buffer needs to be freed.
+ */
+static void *fpga_mgr_parse_header_sg(struct fpga_manager *mgr,
+ struct fpga_image_info *info,
+ struct sg_table *sgt, size_t *ret_size)
+{
+ size_t len, new_header_size, header_size = 0;
+ char *new_buf, *buf = NULL;
+ int ret;
+
+ do {
+ new_header_size = info->header_size;
+ if (new_header_size <= header_size) {
+ dev_err(&mgr->dev, "Requested invalid header size\n");
+ ret = -EFAULT;
+ break;
+ }
+
+ new_buf = krealloc(buf, new_header_size, GFP_KERNEL);
+ if (!new_buf) {
+ ret = -ENOMEM;
+ break;
+ }
+
+ buf = new_buf;
+
+ len = sg_pcopy_to_buffer(sgt->sgl, sgt->nents,
+ buf + header_size,
+ new_header_size - header_size,
+ header_size);
+ if (len != new_header_size - header_size) {
+ ret = -EFAULT;
+ break;
+ }
+
+ header_size = new_header_size;
+ ret = fpga_mgr_parse_header(mgr, info, buf, header_size);
+ } while (ret == -EAGAIN);
+
+ if (ret) {
+ dev_err(&mgr->dev, "Error while parsing FPGA image header\n");
+ mgr->state = FPGA_MGR_STATE_PARSE_HEADER_ERR;
+ kfree(buf);
+ buf = ERR_PTR(ret);
+ }
+
+ *ret_size = header_size;
+
+ return buf;
+}
+
+/*
+ * Call the low level driver's write_init function. This will do the
* device-specific things to get the FPGA into the state where it is ready to
- * receive an FPGA image. The low level driver only gets to see the first
- * initial_header_size bytes in the buffer.
+ * receive an FPGA image. The low level driver gets to see at least first
+ * info->header_size bytes in the buffer. If info->header_size is 0,
+ * write_init will not get any bytes of image buffer.
*/
static int fpga_mgr_write_init_buf(struct fpga_manager *mgr,
struct fpga_image_info *info,
const char *buf, size_t count)
{
+ size_t header_size = info->header_size;
int ret;
mgr->state = FPGA_MGR_STATE_WRITE_INIT;
- if (!mgr->mops->initial_header_size)
+
+ if (header_size > count)
+ ret = -EINVAL;
+ else if (!header_size)
ret = fpga_mgr_write_init(mgr, info, NULL, 0);
else
- ret = fpga_mgr_write_init(
- mgr, info, buf, min(mgr->mops->initial_header_size, count));
+ ret = fpga_mgr_write_init(mgr, info, buf, count);
if (ret) {
dev_err(&mgr->dev, "Error preparing FPGA for writing\n");
@@ -163,39 +290,50 @@ static int fpga_mgr_write_init_buf(struct fpga_manager *mgr,
return 0;
}
-static int fpga_mgr_write_init_sg(struct fpga_manager *mgr,
- struct fpga_image_info *info,
- struct sg_table *sgt)
+static int fpga_mgr_prepare_sg(struct fpga_manager *mgr,
+ struct fpga_image_info *info,
+ struct sg_table *sgt)
{
struct sg_mapping_iter miter;
size_t len;
char *buf;
int ret;
- if (!mgr->mops->initial_header_size)
+ /* Short path. Low level driver don't care about image header. */
+ if (!mgr->mops->initial_header_size && !mgr->mops->parse_header)
return fpga_mgr_write_init_buf(mgr, info, NULL, 0);
/*
* First try to use miter to map the first fragment to access the
* header, this is the typical path.
*/
- sg_miter_start(&miter, sgt->sgl, sgt->nents, SG_MITER_FROM_SG);
- if (sg_miter_next(&miter) &&
- miter.length >= mgr->mops->initial_header_size) {
- ret = fpga_mgr_write_init_buf(mgr, info, miter.addr,
- miter.length);
+ ret = fpga_mgr_parse_header_sg_first(mgr, info, sgt);
+ /* If 0, header fits first fragment, call write_init on it */
+ if (!ret) {
+ sg_miter_start(&miter, sgt->sgl, sgt->nents, SG_MITER_FROM_SG);
+ if (sg_miter_next(&miter)) {
+ ret = fpga_mgr_write_init_buf(mgr, info, miter.addr,
+ miter.length);
+ sg_miter_stop(&miter);
+ return ret;
+ }
sg_miter_stop(&miter);
+ /*
+ * If -EAGAIN, more sg buffer is needed,
+ * otherwise an error has occurred.
+ */
+ } else if (ret != -EAGAIN) {
return ret;
}
- sg_miter_stop(&miter);
- /* Otherwise copy the fragments into temporary memory. */
- buf = kmalloc(mgr->mops->initial_header_size, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
+ /*
+ * Copy the fragments into temporary memory.
+ * Copying is done inside fpga_mgr_parse_header_sg().
+ */
+ buf = fpga_mgr_parse_header_sg(mgr, info, sgt, &len);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
- len = sg_copy_to_buffer(sgt->sgl, sgt->nents, buf,
- mgr->mops->initial_header_size);
ret = fpga_mgr_write_init_buf(mgr, info, buf, len);
kfree(buf);
@@ -226,7 +364,7 @@ static int fpga_mgr_buf_load_sg(struct fpga_manager *mgr,
{
int ret;
- ret = fpga_mgr_write_init_sg(mgr, info, sgt);
+ ret = fpga_mgr_prepare_sg(mgr, info, sgt);
if (ret)
return ret;
@@ -235,17 +373,35 @@ static int fpga_mgr_buf_load_sg(struct fpga_manager *mgr,
if (mgr->mops->write_sg) {
ret = fpga_mgr_write_sg(mgr, sgt);
} else {
+ size_t length, count = 0, data_size = info->data_size;
struct sg_mapping_iter miter;
sg_miter_start(&miter, sgt->sgl, sgt->nents, SG_MITER_FROM_SG);
+
+ if (mgr->mops->skip_header &&
+ !sg_miter_skip(&miter, info->header_size)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
while (sg_miter_next(&miter)) {
- ret = fpga_mgr_write(mgr, miter.addr, miter.length);
+ if (data_size)
+ length = min(miter.length, data_size - count);
+ else
+ length = miter.length;
+
+ ret = fpga_mgr_write(mgr, miter.addr, length);
if (ret)
break;
+
+ count += length;
+ if (data_size && count >= data_size)
+ break;
}
sg_miter_stop(&miter);
}
+out:
if (ret) {
dev_err(&mgr->dev, "Error while writing image data to FPGA\n");
mgr->state = FPGA_MGR_STATE_WRITE_ERR;
@@ -261,10 +417,22 @@ static int fpga_mgr_buf_load_mapped(struct fpga_manager *mgr,
{
int ret;
+ ret = fpga_mgr_parse_header_mapped(mgr, info, buf, count);
+ if (ret)
+ return ret;
+
ret = fpga_mgr_write_init_buf(mgr, info, buf, count);
if (ret)
return ret;
+ if (mgr->mops->skip_header) {
+ buf += info->header_size;
+ count -= info->header_size;
+ }
+
+ if (info->data_size)
+ count = info->data_size;
+
/*
* Write the FPGA image to the FPGA.
*/
@@ -403,6 +571,8 @@ static int fpga_mgr_firmware_load(struct fpga_manager *mgr,
*/
int fpga_mgr_load(struct fpga_manager *mgr, struct fpga_image_info *info)
{
+ info->header_size = mgr->mops->initial_header_size;
+
if (info->sgt)
return fpga_mgr_buf_load_sg(mgr, info, info->sgt);
if (info->buf && info->count)
@@ -423,6 +593,10 @@ static const char * const state_str[] = {
[FPGA_MGR_STATE_FIRMWARE_REQ] = "firmware request",
[FPGA_MGR_STATE_FIRMWARE_REQ_ERR] = "firmware request error",
+ /* Parse FPGA image header */
+ [FPGA_MGR_STATE_PARSE_HEADER] = "parse header",
+ [FPGA_MGR_STATE_PARSE_HEADER_ERR] = "parse header error",
+
/* Preparing FPGA to receive image */
[FPGA_MGR_STATE_WRITE_INIT] = "write init",
[FPGA_MGR_STATE_WRITE_INIT_ERR] = "write init error",
@@ -490,20 +664,16 @@ static struct attribute *fpga_mgr_attrs[] = {
};
ATTRIBUTE_GROUPS(fpga_mgr);
-static struct fpga_manager *__fpga_mgr_get(struct device *dev)
+static struct fpga_manager *__fpga_mgr_get(struct device *mgr_dev)
{
struct fpga_manager *mgr;
- mgr = to_fpga_manager(dev);
+ mgr = to_fpga_manager(mgr_dev);
- if (!try_module_get(dev->parent->driver->owner))
- goto err_dev;
+ if (!try_module_get(mgr->mops_owner))
+ mgr = ERR_PTR(-ENODEV);
return mgr;
-
-err_dev:
- put_device(dev);
- return ERR_PTR(-ENODEV);
}
static int fpga_mgr_dev_match(struct device *dev, const void *data)
@@ -519,12 +689,18 @@ static int fpga_mgr_dev_match(struct device *dev, const void *data)
*/
struct fpga_manager *fpga_mgr_get(struct device *dev)
{
- struct device *mgr_dev = class_find_device(fpga_mgr_class, NULL, dev,
- fpga_mgr_dev_match);
+ struct fpga_manager *mgr;
+ struct device *mgr_dev;
+
+ mgr_dev = class_find_device(&fpga_mgr_class, NULL, dev, fpga_mgr_dev_match);
if (!mgr_dev)
return ERR_PTR(-ENODEV);
- return __fpga_mgr_get(mgr_dev);
+ mgr = __fpga_mgr_get(mgr_dev);
+ if (IS_ERR(mgr))
+ put_device(mgr_dev);
+
+ return mgr;
}
EXPORT_SYMBOL_GPL(fpga_mgr_get);
@@ -537,13 +713,18 @@ EXPORT_SYMBOL_GPL(fpga_mgr_get);
*/
struct fpga_manager *of_fpga_mgr_get(struct device_node *node)
{
- struct device *dev;
+ struct fpga_manager *mgr;
+ struct device *mgr_dev;
- dev = class_find_device_by_of_node(fpga_mgr_class, node);
- if (!dev)
+ mgr_dev = class_find_device_by_of_node(&fpga_mgr_class, node);
+ if (!mgr_dev)
return ERR_PTR(-ENODEV);
- return __fpga_mgr_get(dev);
+ mgr = __fpga_mgr_get(mgr_dev);
+ if (IS_ERR(mgr))
+ put_device(mgr_dev);
+
+ return mgr;
}
EXPORT_SYMBOL_GPL(of_fpga_mgr_get);
@@ -553,7 +734,7 @@ EXPORT_SYMBOL_GPL(of_fpga_mgr_get);
*/
void fpga_mgr_put(struct fpga_manager *mgr)
{
- module_put(mgr->dev.parent->driver->owner);
+ module_put(mgr->mops_owner);
put_device(&mgr->dev);
}
EXPORT_SYMBOL_GPL(fpga_mgr_put);
@@ -592,9 +773,10 @@ void fpga_mgr_unlock(struct fpga_manager *mgr)
EXPORT_SYMBOL_GPL(fpga_mgr_unlock);
/**
- * fpga_mgr_register_full - create and register an FPGA Manager device
+ * __fpga_mgr_register_full - create and register an FPGA Manager device
* @parent: fpga manager device from pdev
* @info: parameters for fpga manager
+ * @owner: owner module containing the ops
*
* The caller of this function is responsible for calling fpga_mgr_unregister().
* Using devm_fpga_mgr_register_full() instead is recommended.
@@ -602,7 +784,8 @@ EXPORT_SYMBOL_GPL(fpga_mgr_unlock);
* Return: pointer to struct fpga_manager pointer or ERR_PTR()
*/
struct fpga_manager *
-fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *info)
+__fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *info,
+ struct module *owner)
{
const struct fpga_manager_ops *mops = info->mops;
struct fpga_manager *mgr;
@@ -622,7 +805,7 @@ fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *in
if (!mgr)
return ERR_PTR(-ENOMEM);
- id = ida_simple_get(&fpga_mgr_ida, 0, 0, GFP_KERNEL);
+ id = ida_alloc(&fpga_mgr_ida, GFP_KERNEL);
if (id < 0) {
ret = id;
goto error_kfree;
@@ -630,12 +813,14 @@ fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *in
mutex_init(&mgr->ref_mutex);
+ mgr->mops_owner = owner;
+
mgr->name = info->name;
mgr->mops = info->mops;
mgr->priv = info->priv;
mgr->compat_id = info->compat_id;
- mgr->dev.class = fpga_mgr_class;
+ mgr->dev.class = &fpga_mgr_class;
mgr->dev.groups = mops->groups;
mgr->dev.parent = parent;
mgr->dev.of_node = parent->of_node;
@@ -661,20 +846,21 @@ fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *in
return mgr;
error_device:
- ida_simple_remove(&fpga_mgr_ida, id);
+ ida_free(&fpga_mgr_ida, id);
error_kfree:
kfree(mgr);
return ERR_PTR(ret);
}
-EXPORT_SYMBOL_GPL(fpga_mgr_register_full);
+EXPORT_SYMBOL_GPL(__fpga_mgr_register_full);
/**
- * fpga_mgr_register - create and register an FPGA Manager device
+ * __fpga_mgr_register - create and register an FPGA Manager device
* @parent: fpga manager device from pdev
* @name: fpga manager name
* @mops: pointer to structure of fpga manager ops
* @priv: fpga manager private data
+ * @owner: owner module containing the ops
*
* The caller of this function is responsible for calling fpga_mgr_unregister().
* Using devm_fpga_mgr_register() instead is recommended. This simple
@@ -685,8 +871,8 @@ EXPORT_SYMBOL_GPL(fpga_mgr_register_full);
* Return: pointer to struct fpga_manager pointer or ERR_PTR()
*/
struct fpga_manager *
-fpga_mgr_register(struct device *parent, const char *name,
- const struct fpga_manager_ops *mops, void *priv)
+__fpga_mgr_register(struct device *parent, const char *name,
+ const struct fpga_manager_ops *mops, void *priv, struct module *owner)
{
struct fpga_manager_info info = { 0 };
@@ -694,9 +880,9 @@ fpga_mgr_register(struct device *parent, const char *name,
info.mops = mops;
info.priv = priv;
- return fpga_mgr_register_full(parent, &info);
+ return __fpga_mgr_register_full(parent, &info, owner);
}
-EXPORT_SYMBOL_GPL(fpga_mgr_register);
+EXPORT_SYMBOL_GPL(__fpga_mgr_register);
/**
* fpga_mgr_unregister - unregister an FPGA manager
@@ -726,15 +912,19 @@ static void devm_fpga_mgr_unregister(struct device *dev, void *res)
}
/**
- * devm_fpga_mgr_register_full - resource managed variant of fpga_mgr_register()
+ * __devm_fpga_mgr_register_full - resource managed variant of fpga_mgr_register()
* @parent: fpga manager device from pdev
* @info: parameters for fpga manager
+ * @owner: owner module containing the ops
+ *
+ * Return: fpga manager pointer on success, negative error code otherwise.
*
* This is the devres variant of fpga_mgr_register_full() for which the unregister
* function will be called automatically when the managing device is detached.
*/
struct fpga_manager *
-devm_fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *info)
+__devm_fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *info,
+ struct module *owner)
{
struct fpga_mgr_devres *dr;
struct fpga_manager *mgr;
@@ -743,7 +933,7 @@ devm_fpga_mgr_register_full(struct device *parent, const struct fpga_manager_inf
if (!dr)
return ERR_PTR(-ENOMEM);
- mgr = fpga_mgr_register_full(parent, info);
+ mgr = __fpga_mgr_register_full(parent, info, owner);
if (IS_ERR(mgr)) {
devres_free(dr);
return mgr;
@@ -754,22 +944,26 @@ devm_fpga_mgr_register_full(struct device *parent, const struct fpga_manager_inf
return mgr;
}
-EXPORT_SYMBOL_GPL(devm_fpga_mgr_register_full);
+EXPORT_SYMBOL_GPL(__devm_fpga_mgr_register_full);
/**
- * devm_fpga_mgr_register - resource managed variant of fpga_mgr_register()
+ * __devm_fpga_mgr_register - resource managed variant of fpga_mgr_register()
* @parent: fpga manager device from pdev
* @name: fpga manager name
* @mops: pointer to structure of fpga manager ops
* @priv: fpga manager private data
+ * @owner: owner module containing the ops
+ *
+ * Return: fpga manager pointer on success, negative error code otherwise.
*
* This is the devres variant of fpga_mgr_register() for which the
* unregister function will be called automatically when the managing
* device is detached.
*/
struct fpga_manager *
-devm_fpga_mgr_register(struct device *parent, const char *name,
- const struct fpga_manager_ops *mops, void *priv)
+__devm_fpga_mgr_register(struct device *parent, const char *name,
+ const struct fpga_manager_ops *mops, void *priv,
+ struct module *owner)
{
struct fpga_manager_info info = { 0 };
@@ -777,35 +971,34 @@ devm_fpga_mgr_register(struct device *parent, const char *name,
info.mops = mops;
info.priv = priv;
- return devm_fpga_mgr_register_full(parent, &info);
+ return __devm_fpga_mgr_register_full(parent, &info, owner);
}
-EXPORT_SYMBOL_GPL(devm_fpga_mgr_register);
+EXPORT_SYMBOL_GPL(__devm_fpga_mgr_register);
static void fpga_mgr_dev_release(struct device *dev)
{
struct fpga_manager *mgr = to_fpga_manager(dev);
- ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
+ ida_free(&fpga_mgr_ida, mgr->dev.id);
kfree(mgr);
}
+static const struct class fpga_mgr_class = {
+ .name = "fpga_manager",
+ .dev_groups = fpga_mgr_groups,
+ .dev_release = fpga_mgr_dev_release,
+};
+
static int __init fpga_mgr_class_init(void)
{
pr_info("FPGA manager framework\n");
- fpga_mgr_class = class_create(THIS_MODULE, "fpga_manager");
- if (IS_ERR(fpga_mgr_class))
- return PTR_ERR(fpga_mgr_class);
-
- fpga_mgr_class->dev_groups = fpga_mgr_groups;
- fpga_mgr_class->dev_release = fpga_mgr_dev_release;
-
- return 0;
+ return class_register(&fpga_mgr_class);
}
static void __exit fpga_mgr_class_exit(void)
{
- class_destroy(fpga_mgr_class);
+ class_unregister(&fpga_mgr_class);
ida_destroy(&fpga_mgr_ida);
}