aboutsummaryrefslogtreecommitdiff
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.c356
1 files changed, 208 insertions, 148 deletions
diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
index c3866816456a..a3595ecc3f79 100644
--- a/drivers/fpga/fpga-mgr.c
+++ b/drivers/fpga/fpga-mgr.c
@@ -21,8 +21,78 @@
static DEFINE_IDA(fpga_mgr_ida);
static struct class *fpga_mgr_class;
+struct fpga_mgr_devres {
+ struct fpga_manager *mgr;
+};
+
+static inline void fpga_mgr_fpga_remove(struct fpga_manager *mgr)
+{
+ if (mgr->mops->fpga_remove)
+ mgr->mops->fpga_remove(mgr);
+}
+
+static inline enum fpga_mgr_states fpga_mgr_state(struct fpga_manager *mgr)
+{
+ if (mgr->mops->state)
+ return mgr->mops->state(mgr);
+ return FPGA_MGR_STATE_UNKNOWN;
+}
+
+static inline u64 fpga_mgr_status(struct fpga_manager *mgr)
+{
+ if (mgr->mops->status)
+ return mgr->mops->status(mgr);
+ return 0;
+}
+
+static inline int fpga_mgr_write(struct fpga_manager *mgr, const char *buf, size_t count)
+{
+ if (mgr->mops->write)
+ return mgr->mops->write(mgr, buf, count);
+ return -EOPNOTSUPP;
+}
+
+/*
+ * After all the FPGA image has been written, do the device specific steps to
+ * finish and set the FPGA into operating mode.
+ */
+static inline int fpga_mgr_write_complete(struct fpga_manager *mgr,
+ struct fpga_image_info *info)
+{
+ int ret = 0;
+
+ mgr->state = FPGA_MGR_STATE_WRITE_COMPLETE;
+ if (mgr->mops->write_complete)
+ ret = mgr->mops->write_complete(mgr, info);
+ if (ret) {
+ dev_err(&mgr->dev, "Error after writing image data to FPGA\n");
+ mgr->state = FPGA_MGR_STATE_WRITE_COMPLETE_ERR;
+ return ret;
+ }
+ mgr->state = FPGA_MGR_STATE_OPERATING;
+
+ return 0;
+}
+
+static inline int fpga_mgr_write_init(struct fpga_manager *mgr,
+ struct fpga_image_info *info,
+ const char *buf, size_t count)
+{
+ if (mgr->mops->write_init)
+ return mgr->mops->write_init(mgr, info, buf, count);
+ return 0;
+}
+
+static inline int fpga_mgr_write_sg(struct fpga_manager *mgr,
+ struct sg_table *sgt)
+{
+ if (mgr->mops->write_sg)
+ return mgr->mops->write_sg(mgr, sgt);
+ return -EOPNOTSUPP;
+}
+
/**
- * fpga_image_info_alloc - Allocate a FPGA image info struct
+ * fpga_image_info_alloc - Allocate an FPGA image info struct
* @dev: owning device
*
* Return: struct fpga_image_info or NULL
@@ -46,7 +116,7 @@ struct fpga_image_info *fpga_image_info_alloc(struct device *dev)
EXPORT_SYMBOL_GPL(fpga_image_info_alloc);
/**
- * fpga_image_info_free - Free a FPGA image info struct
+ * fpga_image_info_free - Free an FPGA image info struct
* @info: FPGA image info struct to free
*/
void fpga_image_info_free(struct fpga_image_info *info)
@@ -78,11 +148,12 @@ static int fpga_mgr_write_init_buf(struct fpga_manager *mgr,
int ret;
mgr->state = FPGA_MGR_STATE_WRITE_INIT;
- if (!mgr->mops->initial_header_size)
- ret = mgr->mops->write_init(mgr, info, NULL, 0);
- else
- ret = mgr->mops->write_init(
- mgr, info, buf, min(mgr->mops->initial_header_size, count));
+ if (!mgr->mops->initial_header_size) {
+ ret = fpga_mgr_write_init(mgr, info, NULL, 0);
+ } else {
+ count = 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");
@@ -133,27 +204,6 @@ static int fpga_mgr_write_init_sg(struct fpga_manager *mgr,
return ret;
}
-/*
- * After all the FPGA image has been written, do the device specific steps to
- * finish and set the FPGA into operating mode.
- */
-static int fpga_mgr_write_complete(struct fpga_manager *mgr,
- struct fpga_image_info *info)
-{
- int ret;
-
- mgr->state = FPGA_MGR_STATE_WRITE_COMPLETE;
- ret = mgr->mops->write_complete(mgr, info);
- if (ret) {
- dev_err(&mgr->dev, "Error after writing image data to FPGA\n");
- mgr->state = FPGA_MGR_STATE_WRITE_COMPLETE_ERR;
- return ret;
- }
- mgr->state = FPGA_MGR_STATE_OPERATING;
-
- return 0;
-}
-
/**
* fpga_mgr_buf_load_sg - load fpga from image in buffer from a scatter list
* @mgr: fpga manager
@@ -184,13 +234,13 @@ static int fpga_mgr_buf_load_sg(struct fpga_manager *mgr,
/* Write the FPGA image to the FPGA. */
mgr->state = FPGA_MGR_STATE_WRITE;
if (mgr->mops->write_sg) {
- ret = mgr->mops->write_sg(mgr, sgt);
+ ret = fpga_mgr_write_sg(mgr, sgt);
} else {
struct sg_mapping_iter miter;
sg_miter_start(&miter, sgt->sgl, sgt->nents, SG_MITER_FROM_SG);
while (sg_miter_next(&miter)) {
- ret = mgr->mops->write(mgr, miter.addr, miter.length);
+ ret = fpga_mgr_write(mgr, miter.addr, miter.length);
if (ret)
break;
}
@@ -220,7 +270,7 @@ static int fpga_mgr_buf_load_mapped(struct fpga_manager *mgr,
* Write the FPGA image to the FPGA.
*/
mgr->state = FPGA_MGR_STATE_WRITE;
- ret = mgr->mops->write(mgr, buf, count);
+ ret = fpga_mgr_write(mgr, buf, count);
if (ret) {
dev_err(&mgr->dev, "Error while writing image data to FPGA\n");
mgr->state = FPGA_MGR_STATE_WRITE_ERR;
@@ -413,10 +463,7 @@ static ssize_t status_show(struct device *dev,
u64 status;
int len = 0;
- if (!mgr->mops->status)
- return -ENOENT;
-
- status = mgr->mops->status(mgr);
+ status = fpga_mgr_status(mgr);
if (status & FPGA_MGR_STATUS_OPERATION_ERR)
len += sprintf(buf + len, "reconfig operation error\n");
@@ -466,7 +513,7 @@ static int fpga_mgr_dev_match(struct device *dev, const void *data)
}
/**
- * fpga_mgr_get - Given a device, get a reference to a fpga mgr.
+ * fpga_mgr_get - Given a device, get a reference to an fpga mgr.
* @dev: parent device that fpga mgr was registered with
*
* Return: fpga manager struct or IS_ERR() condition containing error code.
@@ -482,13 +529,8 @@ struct fpga_manager *fpga_mgr_get(struct device *dev)
}
EXPORT_SYMBOL_GPL(fpga_mgr_get);
-static int fpga_mgr_of_node_match(struct device *dev, const void *data)
-{
- return dev->of_node == data;
-}
-
/**
- * of_fpga_mgr_get - Given a device node, get a reference to a fpga mgr.
+ * of_fpga_mgr_get - Given a device node, get a reference to an fpga mgr.
*
* @node: device node
*
@@ -498,8 +540,7 @@ struct fpga_manager *of_fpga_mgr_get(struct device_node *node)
{
struct device *dev;
- dev = class_find_device(fpga_mgr_class, NULL, node,
- fpga_mgr_of_node_match);
+ dev = class_find_device_by_of_node(fpga_mgr_class, node);
if (!dev)
return ERR_PTR(-ENODEV);
@@ -508,7 +549,7 @@ struct fpga_manager *of_fpga_mgr_get(struct device_node *node)
EXPORT_SYMBOL_GPL(of_fpga_mgr_get);
/**
- * fpga_mgr_put - release a reference to a fpga manager
+ * fpga_mgr_put - release a reference to an fpga manager
* @mgr: fpga manager structure
*/
void fpga_mgr_put(struct fpga_manager *mgr)
@@ -552,39 +593,35 @@ void fpga_mgr_unlock(struct fpga_manager *mgr)
EXPORT_SYMBOL_GPL(fpga_mgr_unlock);
/**
- * fpga_mgr_create - create and initialize a FPGA manager struct
- * @dev: fpga manager device from pdev
- * @name: fpga manager name
- * @mops: pointer to structure of fpga manager ops
- * @priv: fpga manager private data
+ * fpga_mgr_register_full - create and register an FPGA Manager device
+ * @parent: fpga manager device from pdev
+ * @info: parameters for fpga manager
*
- * The caller of this function is responsible for freeing the struct with
- * fpga_mgr_free(). Using devm_fpga_mgr_create() instead is recommended.
+ * The caller of this function is responsible for calling fpga_mgr_unregister().
+ * Using devm_fpga_mgr_register_full() instead is recommended.
*
- * Return: pointer to struct fpga_manager or NULL
+ * Return: pointer to struct fpga_manager pointer or ERR_PTR()
*/
-struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
- const struct fpga_manager_ops *mops,
- void *priv)
+struct fpga_manager *
+fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *info)
{
+ const struct fpga_manager_ops *mops = info->mops;
struct fpga_manager *mgr;
int id, ret;
- if (!mops || !mops->write_complete || !mops->state ||
- !mops->write_init || (!mops->write && !mops->write_sg) ||
- (mops->write && mops->write_sg)) {
- dev_err(dev, "Attempt to register without fpga_manager_ops\n");
- return NULL;
+ if (!mops) {
+ dev_err(parent, "Attempt to register without fpga_manager_ops\n");
+ return ERR_PTR(-EINVAL);
}
- if (!name || !strlen(name)) {
- dev_err(dev, "Attempt to register with no name!\n");
- return NULL;
+ if (!info->name || !strlen(info->name)) {
+ dev_err(parent, "Attempt to register with no name!\n");
+ return ERR_PTR(-EINVAL);
}
mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
if (!mgr)
- return NULL;
+ return ERR_PTR(-ENOMEM);
id = ida_simple_get(&fpga_mgr_ida, 0, 0, GFP_KERNEL);
if (id < 0) {
@@ -594,21 +631,34 @@ struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
mutex_init(&mgr->ref_mutex);
- mgr->name = name;
- mgr->mops = mops;
- mgr->priv = priv;
+ mgr->name = info->name;
+ mgr->mops = info->mops;
+ mgr->priv = info->priv;
+ mgr->compat_id = info->compat_id;
- device_initialize(&mgr->dev);
mgr->dev.class = fpga_mgr_class;
mgr->dev.groups = mops->groups;
- mgr->dev.parent = dev;
- mgr->dev.of_node = dev->of_node;
+ mgr->dev.parent = parent;
+ mgr->dev.of_node = parent->of_node;
mgr->dev.id = id;
ret = dev_set_name(&mgr->dev, "fpga%d", id);
if (ret)
goto error_device;
+ /*
+ * Initialize framework state by requesting low level driver read state
+ * from device. FPGA may be in reset mode or may have been programmed
+ * by bootloader or EEPROM.
+ */
+ mgr->state = fpga_mgr_state(mgr);
+
+ ret = device_register(&mgr->dev);
+ if (ret) {
+ put_device(&mgr->dev);
+ return ERR_PTR(ret);
+ }
+
return mgr;
error_device:
@@ -616,122 +666,132 @@ error_device:
error_kfree:
kfree(mgr);
- return NULL;
+ return ERR_PTR(ret);
}
-EXPORT_SYMBOL_GPL(fpga_mgr_create);
+EXPORT_SYMBOL_GPL(fpga_mgr_register_full);
/**
- * fpga_mgr_free - free a FPGA manager created with fpga_mgr_create()
- * @mgr: fpga manager struct
- */
-void fpga_mgr_free(struct fpga_manager *mgr)
-{
- ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
- kfree(mgr);
-}
-EXPORT_SYMBOL_GPL(fpga_mgr_free);
-
-static void devm_fpga_mgr_release(struct device *dev, void *res)
-{
- struct fpga_manager *mgr = *(struct fpga_manager **)res;
-
- fpga_mgr_free(mgr);
-}
-
-/**
- * devm_fpga_mgr_create - create and initialize a managed FPGA manager struct
- * @dev: fpga manager device from pdev
+ * 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
*
- * This function is intended for use in a FPGA manager driver's probe function.
- * After the manager driver creates the manager struct with
- * devm_fpga_mgr_create(), it should register it with fpga_mgr_register(). The
- * manager driver's remove function should call fpga_mgr_unregister(). The
- * manager struct allocated with this function will be freed automatically on
- * driver detach. This includes the case of a probe function returning error
- * before calling fpga_mgr_register(), the struct will still get cleaned up.
+ * The caller of this function is responsible for calling fpga_mgr_unregister().
+ * Using devm_fpga_mgr_register() instead is recommended. This simple
+ * version of the register function should be sufficient for most users. The
+ * fpga_mgr_register_full() function is available for users that need to pass
+ * additional, optional parameters.
*
- * Return: pointer to struct fpga_manager or NULL
+ * Return: pointer to struct fpga_manager pointer or ERR_PTR()
*/
-struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name,
- const struct fpga_manager_ops *mops,
- void *priv)
+struct fpga_manager *
+fpga_mgr_register(struct device *parent, const char *name,
+ const struct fpga_manager_ops *mops, void *priv)
{
- struct fpga_manager **ptr, *mgr;
-
- ptr = devres_alloc(devm_fpga_mgr_release, sizeof(*ptr), GFP_KERNEL);
- if (!ptr)
- return NULL;
+ struct fpga_manager_info info = { 0 };
- mgr = fpga_mgr_create(dev, name, mops, priv);
- if (!mgr) {
- devres_free(ptr);
- } else {
- *ptr = mgr;
- devres_add(dev, ptr);
- }
+ info.name = name;
+ info.mops = mops;
+ info.priv = priv;
- return mgr;
+ return fpga_mgr_register_full(parent, &info);
}
-EXPORT_SYMBOL_GPL(devm_fpga_mgr_create);
+EXPORT_SYMBOL_GPL(fpga_mgr_register);
/**
- * fpga_mgr_register - register a FPGA manager
+ * fpga_mgr_unregister - unregister an FPGA manager
* @mgr: fpga manager struct
*
- * Return: 0 on success, negative error code otherwise.
+ * This function is intended for use in an FPGA manager driver's remove function.
*/
-int fpga_mgr_register(struct fpga_manager *mgr)
+void fpga_mgr_unregister(struct fpga_manager *mgr)
{
- int ret;
+ dev_info(&mgr->dev, "%s %s\n", __func__, mgr->name);
/*
- * Initialize framework state by requesting low level driver read state
- * from device. FPGA may be in reset mode or may have been programmed
- * by bootloader or EEPROM.
+ * If the low level driver provides a method for putting fpga into
+ * a desired state upon unregister, do it.
*/
- mgr->state = mgr->mops->state(mgr);
+ fpga_mgr_fpga_remove(mgr);
- ret = device_add(&mgr->dev);
- if (ret)
- goto error_device;
+ device_unregister(&mgr->dev);
+}
+EXPORT_SYMBOL_GPL(fpga_mgr_unregister);
- dev_info(&mgr->dev, "%s registered\n", mgr->name);
+static void devm_fpga_mgr_unregister(struct device *dev, void *res)
+{
+ struct fpga_mgr_devres *dr = res;
- return 0;
+ fpga_mgr_unregister(dr->mgr);
+}
-error_device:
- ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
+/**
+ * devm_fpga_mgr_register_full - resource managed variant of fpga_mgr_register()
+ * @parent: fpga manager device from pdev
+ * @info: parameters for fpga manager
+ *
+ * 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)
+{
+ struct fpga_mgr_devres *dr;
+ struct fpga_manager *mgr;
- return ret;
+ dr = devres_alloc(devm_fpga_mgr_unregister, sizeof(*dr), GFP_KERNEL);
+ if (!dr)
+ return ERR_PTR(-ENOMEM);
+
+ mgr = fpga_mgr_register_full(parent, info);
+ if (IS_ERR(mgr)) {
+ devres_free(dr);
+ return mgr;
+ }
+
+ dr->mgr = mgr;
+ devres_add(parent, dr);
+
+ return mgr;
}
-EXPORT_SYMBOL_GPL(fpga_mgr_register);
+EXPORT_SYMBOL_GPL(devm_fpga_mgr_register_full);
/**
- * fpga_mgr_unregister - unregister a FPGA manager
- * @mgr: fpga manager struct
+ * 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
*
- * This function is intended for use in a FPGA manager driver's remove function.
+ * 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.
*/
-void fpga_mgr_unregister(struct fpga_manager *mgr)
+struct fpga_manager *
+devm_fpga_mgr_register(struct device *parent, const char *name,
+ const struct fpga_manager_ops *mops, void *priv)
{
- dev_info(&mgr->dev, "%s %s\n", __func__, mgr->name);
+ struct fpga_manager_info info = { 0 };
- /*
- * If the low level driver provides a method for putting fpga into
- * a desired state upon unregister, do it.
- */
- if (mgr->mops->fpga_remove)
- mgr->mops->fpga_remove(mgr);
+ info.name = name;
+ info.mops = mops;
+ info.priv = priv;
- device_unregister(&mgr->dev);
+ return devm_fpga_mgr_register_full(parent, &info);
}
-EXPORT_SYMBOL_GPL(fpga_mgr_unregister);
+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);
+ kfree(mgr);
}
static int __init fpga_mgr_class_init(void)