linux-fpga.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v5 0/3] fpga: Use standard class dev_release function
@ 2021-06-16 22:57 Russ Weight
  2021-06-16 22:57 ` [PATCH v5 1/3] fpga: mgr: Use standard dev_release for class driver Russ Weight
                   ` (2 more replies)
  0 siblings, 3 replies; 19+ messages in thread
From: Russ Weight @ 2021-06-16 22:57 UTC (permalink / raw)
  To: mdf, linux-fpga
  Cc: trix, lgoncalv, yilun.xu, hao.wu, matthew.gerlach, richard.gong,
	Russ Weight

The FPGA framework has a convention of using managed resource
functions to allow parent drivers to manage the data structures
allocated by the class drivers. They use an empty *_dev_release()
function to satisfy the class driver.

This is inconsistent with linux driver model.

These changes remove the managed resource functions and populate
the class dev_release callback functions. They also merge the
create and register functions into a single register function for
each of the fpga-mgr, fpga-region, and fpga-bridge class drivers.

For more context, refer to this email thread:

https://marc.info/?l=linux-fpga&m=162127412218557&w=2

I turned on the configs assocated with each of the modified files,
but I must have been missing some dependencies, because not all
of them compiled. I did a run-time test specifically with the
dfl-fme infrastructure. This would have exercised the region,
bridge, and fpga-mgr frameworks.

Changelog v4 -> v5:
  - Rebased on top of recently accepted patches.
  - Removed compat_id from the fpga_mgr_register() parameter list
    and added it to the fpga_manager_ops structure. This also required
    dynamically allocating the dfl-fme-ops structure in order to add
    the appropriate compat_id.
  - Created the fpga_region_ops data structure which is optionally passed
    to fpga_region_register(). compat_id, the get_bridges() pointer, and
    the priv pointer are included in the fpga_region_ops structure.

Changelog v3 -> v4:
  - Added the compat_id parameter to fpga_mgr_register() and
    devm_fpga_mgr_register() to ensure that the compat_id is set before
    the device_register() call.
  - Added the compat_id parameter to fpga_region_register() to ensure
    that the compat_id is set before the device_register() call.
  - Modified the dfl_fpga_feature_devs_enumerate() function to restore
    the fpga_region_register() call to the correct location.

Changelog v2 -> v3:
  - Cleaned up comment headers for fpga_mgr_register(), fpga_bridge_register(),
    and fpga_region_register().
  - Fixed error return on ida_simple_get() failure for fpga_mgr_register(),
    fpga_bridge_register(), and fpga_region_register().
  - Fixed error return value for fpga_bridge_register(): ERR_PTR(ret) instead
    of NULL.

Changelog v1 -> v2:
  - Restored devm_fpga_mgr_register() functionality to the fpga-mgr
    class driver, adapted for the combined create/register functionality.
  - All previous callers of devm_fpga_mgr_register() will continue to call
    devm_fpga_mgr_register().
  - replaced unnecessary ternary operators in return statements with
    standard if conditions.

Russ Weight (3):
  fpga: mgr: Use standard dev_release for class driver
  fpga: bridge: Use standard dev_release for class driver
  fpga: region: Use standard dev_release for class driver

 drivers/fpga/altera-cvp.c           |  12 +-
 drivers/fpga/altera-fpga2sdram.c    |  12 +-
 drivers/fpga/altera-freeze-bridge.c |  10 +-
 drivers/fpga/altera-hps2fpga.c      |  12 +-
 drivers/fpga/altera-pr-ip-core.c    |   8 +-
 drivers/fpga/altera-ps-spi.c        |  10 +-
 drivers/fpga/dfl-fme-br.c           |  10 +-
 drivers/fpga/dfl-fme-mgr.c          |  52 +++++----
 drivers/fpga/dfl-fme-pr.c           |   2 +-
 drivers/fpga/dfl-fme-region.c       |  32 ++++--
 drivers/fpga/dfl.c                  |  12 +-
 drivers/fpga/fpga-bridge.c          | 116 ++++----------------
 drivers/fpga/fpga-mgr.c             | 163 +++++++---------------------
 drivers/fpga/fpga-region.c          | 119 +++++---------------
 drivers/fpga/ice40-spi.c            |  10 +-
 drivers/fpga/machxo2-spi.c          |  10 +-
 drivers/fpga/of-fpga-region.c       |  14 +--
 drivers/fpga/socfpga-a10.c          |  16 +--
 drivers/fpga/socfpga.c              |  10 +-
 drivers/fpga/stratix10-soc.c        |  16 +--
 drivers/fpga/ts73xx-fpga.c          |  10 +-
 drivers/fpga/xilinx-pr-decoupler.c  |  17 +--
 drivers/fpga/xilinx-spi.c           |  12 +-
 drivers/fpga/zynq-fpga.c            |  16 +--
 drivers/fpga/zynqmp-fpga.c          |  10 +-
 include/linux/fpga/fpga-bridge.h    |  12 +-
 include/linux/fpga/fpga-mgr.h       |  43 ++++----
 include/linux/fpga/fpga-region.h    |  38 ++++---
 28 files changed, 286 insertions(+), 518 deletions(-)

-- 
2.25.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v5 1/3] fpga: mgr: Use standard dev_release for class driver
  2021-06-16 22:57 [PATCH v5 0/3] fpga: Use standard class dev_release function Russ Weight
@ 2021-06-16 22:57 ` Russ Weight
  2021-06-18 15:45   ` Xu Yilun
  2021-06-16 22:57 ` [PATCH v5 2/3] fpga: bridge: " Russ Weight
  2021-06-16 22:57 ` [PATCH v5 3/3] fpga: region: " Russ Weight
  2 siblings, 1 reply; 19+ messages in thread
From: Russ Weight @ 2021-06-16 22:57 UTC (permalink / raw)
  To: mdf, linux-fpga
  Cc: trix, lgoncalv, yilun.xu, hao.wu, matthew.gerlach, richard.gong,
	Russ Weight

The FPGA manager class driver data structure is being treated as a
managed resource instead of using standard dev_release call-back
to release the class data structure. This change removes the
managed resource code for the freeing of the class data structure
and combines the create() and register() functions into a single
register() function.

The devm_fpga_mgr_register() function is retained.

Signed-off-by: Russ Weight <russell.h.weight@intel.com>
---
v5:
  - Rebased on top of recently accepted patches.
  - Removed compat_id from the fpga_mgr_register() parameter list
    and added it to the fpga_manager_ops structure. This also required
    dynamically allocating the dfl-fme-ops structure in order to add
    the appropriate compat_id.
v4:
  - Added the compat_id parameter to fpga_mgr_register() and
    devm_fpga_mgr_register() to ensure that the compat_id is set before
    the device_register() call.
v3:
  - Cleaned up comment header for fpga_mgr_register()
  - Fix error return on ida_simple_get() failure
v2:
  - Restored devm_fpga_mgr_register() functionality, adapted for the combined
    create/register functionality.
  - All previous callers of devm_fpga_mgr_register() will continue to call
    devm_fpga_mgr_register().
  - replaced unnecessary ternary operators in return statements with standard
    if conditions.
---
 drivers/fpga/altera-cvp.c        |  12 +--
 drivers/fpga/altera-pr-ip-core.c |   8 +-
 drivers/fpga/altera-ps-spi.c     |  10 +-
 drivers/fpga/dfl-fme-mgr.c       |  52 ++++++----
 drivers/fpga/dfl-fme-region.c    |   2 +-
 drivers/fpga/fpga-mgr.c          | 163 ++++++++-----------------------
 drivers/fpga/ice40-spi.c         |  10 +-
 drivers/fpga/machxo2-spi.c       |  10 +-
 drivers/fpga/socfpga-a10.c       |  16 ++-
 drivers/fpga/socfpga.c           |  10 +-
 drivers/fpga/stratix10-soc.c     |  16 +--
 drivers/fpga/ts73xx-fpga.c       |  10 +-
 drivers/fpga/xilinx-spi.c        |  12 +--
 drivers/fpga/zynq-fpga.c         |  16 ++-
 drivers/fpga/zynqmp-fpga.c       |  10 +-
 include/linux/fpga/fpga-mgr.h    |  43 ++++----
 16 files changed, 153 insertions(+), 247 deletions(-)

diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
index ccf4546eff29..4ffb9da537d8 100644
--- a/drivers/fpga/altera-cvp.c
+++ b/drivers/fpga/altera-cvp.c
@@ -652,19 +652,15 @@ static int altera_cvp_probe(struct pci_dev *pdev,
 	snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s @%s",
 		 ALTERA_CVP_MGR_NAME, pci_name(pdev));
 
-	mgr = devm_fpga_mgr_create(&pdev->dev, conf->mgr_name,
-				   &altera_cvp_ops, conf);
-	if (!mgr) {
-		ret = -ENOMEM;
+	mgr = fpga_mgr_register(&pdev->dev, conf->mgr_name,
+				&altera_cvp_ops, conf);
+	if (IS_ERR(mgr)) {
+		ret = PTR_ERR(mgr);
 		goto err_unmap;
 	}
 
 	pci_set_drvdata(pdev, mgr);
 
-	ret = fpga_mgr_register(mgr);
-	if (ret)
-		goto err_unmap;
-
 	return 0;
 
 err_unmap:
diff --git a/drivers/fpga/altera-pr-ip-core.c b/drivers/fpga/altera-pr-ip-core.c
index dfdf21ed34c4..17babf974852 100644
--- a/drivers/fpga/altera-pr-ip-core.c
+++ b/drivers/fpga/altera-pr-ip-core.c
@@ -191,11 +191,11 @@ int alt_pr_register(struct device *dev, void __iomem *reg_base)
 		(val & ALT_PR_CSR_STATUS_MSK) >> ALT_PR_CSR_STATUS_SFT,
 		(int)(val & ALT_PR_CSR_PR_START));
 
-	mgr = devm_fpga_mgr_create(dev, dev_name(dev), &alt_pr_ops, priv);
-	if (!mgr)
-		return -ENOMEM;
+	mgr = devm_fpga_mgr_register(dev, dev_name(dev), &alt_pr_ops, priv);
+	if (IS_ERR(mgr))
+		return PTR_ERR(mgr);
 
-	return devm_fpga_mgr_register(dev, mgr);
+	return 0;
 }
 EXPORT_SYMBOL_GPL(alt_pr_register);
 
diff --git a/drivers/fpga/altera-ps-spi.c b/drivers/fpga/altera-ps-spi.c
index 23bfd4d1ad0f..d3f77b0312b2 100644
--- a/drivers/fpga/altera-ps-spi.c
+++ b/drivers/fpga/altera-ps-spi.c
@@ -302,12 +302,12 @@ static int altera_ps_probe(struct spi_device *spi)
 	snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s %s",
 		 dev_driver_string(&spi->dev), dev_name(&spi->dev));
 
-	mgr = devm_fpga_mgr_create(&spi->dev, conf->mgr_name,
-				   &altera_ps_ops, conf);
-	if (!mgr)
-		return -ENOMEM;
+	mgr = devm_fpga_mgr_register(&spi->dev, conf->mgr_name,
+				     &altera_ps_ops, conf);
+	if (IS_ERR(mgr))
+		return PTR_ERR(mgr);
 
-	return devm_fpga_mgr_register(&spi->dev, mgr);
+	return 0;
 }
 
 static const struct spi_device_id altera_ps_spi_ids[] = {
diff --git a/drivers/fpga/dfl-fme-mgr.c b/drivers/fpga/dfl-fme-mgr.c
index d5861d13b306..1b93bc292dbe 100644
--- a/drivers/fpga/dfl-fme-mgr.c
+++ b/drivers/fpga/dfl-fme-mgr.c
@@ -264,14 +264,6 @@ static u64 fme_mgr_status(struct fpga_manager *mgr)
 	return pr_error_to_mgr_status(priv->pr_error);
 }
 
-static const struct fpga_manager_ops fme_mgr_ops = {
-	.write_init = fme_mgr_write_init,
-	.write = fme_mgr_write,
-	.write_complete = fme_mgr_write_complete,
-	.state = fme_mgr_state,
-	.status = fme_mgr_status,
-};
-
 static void fme_mgr_get_compat_id(void __iomem *fme_pr,
 				  struct fpga_compat_id *id)
 {
@@ -279,10 +271,34 @@ static void fme_mgr_get_compat_id(void __iomem *fme_pr,
 	id->id_h = readq(fme_pr + FME_PR_INTFC_ID_H);
 }
 
+static struct fpga_manager_ops *fme_mgr_get_ops(struct device *dev,
+						struct fme_mgr_priv *priv)
+{
+	struct fpga_manager_ops *ops;
+
+	ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL);
+	if (!ops)
+		return NULL;
+
+	ops->compat_id = devm_kzalloc(dev, sizeof(struct fpga_compat_id),
+				      GFP_KERNEL);
+	if (!ops->compat_id)
+		return NULL;
+
+	fme_mgr_get_compat_id(priv->ioaddr, ops->compat_id);
+	ops->write_init = fme_mgr_write_init;
+	ops->write = fme_mgr_write;
+	ops->write_complete = fme_mgr_write_complete;
+	ops->state = fme_mgr_state;
+	ops->status = fme_mgr_status;
+
+	return ops;
+}
+
 static int fme_mgr_probe(struct platform_device *pdev)
 {
 	struct dfl_fme_mgr_pdata *pdata = dev_get_platdata(&pdev->dev);
-	struct fpga_compat_id *compat_id;
+	struct fpga_manager_ops *fme_mgr_ops;
 	struct device *dev = &pdev->dev;
 	struct fme_mgr_priv *priv;
 	struct fpga_manager *mgr;
@@ -302,20 +318,16 @@ static int fme_mgr_probe(struct platform_device *pdev)
 			return PTR_ERR(priv->ioaddr);
 	}
 
-	compat_id = devm_kzalloc(dev, sizeof(*compat_id), GFP_KERNEL);
-	if (!compat_id)
-		return -ENOMEM;
-
-	fme_mgr_get_compat_id(priv->ioaddr, compat_id);
-
-	mgr = devm_fpga_mgr_create(dev, "DFL FME FPGA Manager",
-				   &fme_mgr_ops, priv);
-	if (!mgr)
+	fme_mgr_ops = fme_mgr_get_ops(dev, priv);
+	if (!fme_mgr_ops)
 		return -ENOMEM;
 
-	mgr->compat_id = compat_id;
+	mgr = devm_fpga_mgr_register(dev, "DFL FME FPGA Manager",
+				     fme_mgr_ops, priv);
+	if (IS_ERR(mgr))
+		return PTR_ERR(mgr);
 
-	return devm_fpga_mgr_register(dev, mgr);
+	return 0;
 }
 
 static struct platform_driver fme_mgr_driver = {
diff --git a/drivers/fpga/dfl-fme-region.c b/drivers/fpga/dfl-fme-region.c
index 1eeb42af1012..a64c13e198a0 100644
--- a/drivers/fpga/dfl-fme-region.c
+++ b/drivers/fpga/dfl-fme-region.c
@@ -46,7 +46,7 @@ static int fme_region_probe(struct platform_device *pdev)
 	}
 
 	region->priv = pdata;
-	region->compat_id = mgr->compat_id;
+	region->compat_id = mgr->mops->compat_id;
 	platform_set_drvdata(pdev, region);
 
 	ret = fpga_region_register(region);
diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
index ecb4c3c795fa..9aecfea432c8 100644
--- a/drivers/fpga/fpga-mgr.c
+++ b/drivers/fpga/fpga-mgr.c
@@ -550,20 +550,20 @@ void fpga_mgr_unlock(struct fpga_manager *mgr)
 EXPORT_SYMBOL_GPL(fpga_mgr_unlock);
 
 /**
- * fpga_mgr_create - create and initialize an FPGA manager struct
+ * 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
  *
- * 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 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 *parent, 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 *mgr;
 	int id, ret;
@@ -572,21 +572,23 @@ struct fpga_manager *fpga_mgr_create(struct device *parent, const char *name,
 	    !mops->write_init || (!mops->write && !mops->write_sg) ||
 	    (mops->write && mops->write_sg)) {
 		dev_err(parent, "Attempt to register without fpga_manager_ops\n");
-		return NULL;
+		return ERR_PTR(-EINVAL);
 	}
 
 	if (!name || !strlen(name)) {
 		dev_err(parent, "Attempt to register with no name!\n");
-		return NULL;
+		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)
+	if (id < 0) {
+		ret = id;
 		goto error_kfree;
+	}
 
 	mutex_init(&mgr->ref_mutex);
 
@@ -594,7 +596,6 @@ struct fpga_manager *fpga_mgr_create(struct device *parent, const char *name,
 	mgr->mops = mops;
 	mgr->priv = priv;
 
-	device_initialize(&mgr->dev);
 	mgr->dev.class = fpga_mgr_class;
 	mgr->dev.groups = mops->groups;
 	mgr->dev.parent = parent;
@@ -605,84 +606,6 @@ struct fpga_manager *fpga_mgr_create(struct device *parent, const char *name,
 	if (ret)
 		goto error_device;
 
-	return mgr;
-
-error_device:
-	ida_simple_remove(&fpga_mgr_ida, id);
-error_kfree:
-	kfree(mgr);
-
-	return NULL;
-}
-EXPORT_SYMBOL_GPL(fpga_mgr_create);
-
-/**
- * fpga_mgr_free - free an 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_mgr_devres *dr = res;
-
-	fpga_mgr_free(dr->mgr);
-}
-
-/**
- * devm_fpga_mgr_create - create and initialize a managed FPGA manager struct
- * @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 an 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.
- *
- * Return: pointer to struct fpga_manager or NULL
- */
-struct fpga_manager *devm_fpga_mgr_create(struct device *parent, const char *name,
-					  const struct fpga_manager_ops *mops,
-					  void *priv)
-{
-	struct fpga_mgr_devres *dr;
-
-	dr = devres_alloc(devm_fpga_mgr_release, sizeof(*dr), GFP_KERNEL);
-	if (!dr)
-		return NULL;
-
-	dr->mgr = fpga_mgr_create(parent, name, mops, priv);
-	if (!dr->mgr) {
-		devres_free(dr);
-		return NULL;
-	}
-
-	devres_add(parent, dr);
-
-	return dr->mgr;
-}
-EXPORT_SYMBOL_GPL(devm_fpga_mgr_create);
-
-/**
- * fpga_mgr_register - register an FPGA manager
- * @mgr: fpga manager struct
- *
- * Return: 0 on success, negative error code otherwise.
- */
-int fpga_mgr_register(struct fpga_manager *mgr)
-{
-	int ret;
-
 	/*
 	 * Initialize framework state by requesting low level driver read state
 	 * from device.  FPGA may be in reset mode or may have been programmed
@@ -690,18 +613,20 @@ int fpga_mgr_register(struct fpga_manager *mgr)
 	 */
 	mgr->state = mgr->mops->state(mgr);
 
-	ret = device_add(&mgr->dev);
-	if (ret)
-		goto error_device;
-
-	dev_info(&mgr->dev, "%s registered\n", mgr->name);
+	ret = device_register(&mgr->dev);
+	if (ret) {
+		put_device(&mgr->dev);
+		return ERR_PTR(ret);
+	}
 
-	return 0;
+	return mgr;
 
 error_device:
-	ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
+	ida_simple_remove(&fpga_mgr_ida, id);
+error_kfree:
+	kfree(mgr);
 
-	return ret;
+	return ERR_PTR(ret);
 }
 EXPORT_SYMBOL_GPL(fpga_mgr_register);
 
@@ -726,14 +651,6 @@ void fpga_mgr_unregister(struct fpga_manager *mgr)
 }
 EXPORT_SYMBOL_GPL(fpga_mgr_unregister);
 
-static int fpga_mgr_devres_match(struct device *dev, void *res,
-				 void *match_data)
-{
-	struct fpga_mgr_devres *dr = res;
-
-	return match_data == dr->mgr;
-}
-
 static void devm_fpga_mgr_unregister(struct device *dev, void *res)
 {
 	struct fpga_mgr_devres *dr = res;
@@ -743,44 +660,44 @@ static void devm_fpga_mgr_unregister(struct device *dev, void *res)
 
 /**
  * devm_fpga_mgr_register - resource managed variant of fpga_mgr_register()
- * @dev: managing device for this FPGA manager
- * @mgr: 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
  *
  * This is the devres variant of fpga_mgr_register() for which the unregister
  * function will be called automatically when the managing device is detached.
  */
-int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr)
+struct fpga_manager *
+devm_fpga_mgr_register(struct device *dev, const char *name,
+		       const struct fpga_manager_ops *mops, void *priv)
 {
 	struct fpga_mgr_devres *dr;
-	int ret;
-
-	/*
-	 * Make sure that the struct fpga_manager * that is passed in is
-	 * managed itself.
-	 */
-	if (WARN_ON(!devres_find(dev, devm_fpga_mgr_release,
-				 fpga_mgr_devres_match, mgr)))
-		return -EINVAL;
+	struct fpga_manager *mgr;
 
 	dr = devres_alloc(devm_fpga_mgr_unregister, sizeof(*dr), GFP_KERNEL);
 	if (!dr)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
-	ret = fpga_mgr_register(mgr);
-	if (ret) {
+	mgr = fpga_mgr_register(dev, name, mops, priv);
+	if (IS_ERR(mgr)) {
 		devres_free(dr);
-		return ret;
+		return mgr;
 	}
 
 	dr->mgr = mgr;
 	devres_add(dev, dr);
 
-	return 0;
+	return mgr;
 }
 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)
diff --git a/drivers/fpga/ice40-spi.c b/drivers/fpga/ice40-spi.c
index 69dec5af23c3..91ac25f339f3 100644
--- a/drivers/fpga/ice40-spi.c
+++ b/drivers/fpga/ice40-spi.c
@@ -178,12 +178,12 @@ static int ice40_fpga_probe(struct spi_device *spi)
 		return ret;
 	}
 
-	mgr = devm_fpga_mgr_create(dev, "Lattice iCE40 FPGA Manager",
-				   &ice40_fpga_ops, priv);
-	if (!mgr)
-		return -ENOMEM;
+	mgr = devm_fpga_mgr_register(dev, "Lattice iCE40 FPGA Manager",
+				     &ice40_fpga_ops, priv);
+	if (IS_ERR(mgr))
+		return PTR_ERR(mgr);
 
-	return devm_fpga_mgr_register(dev, mgr);
+	return 0;
 }
 
 static const struct of_device_id ice40_fpga_of_match[] = {
diff --git a/drivers/fpga/machxo2-spi.c b/drivers/fpga/machxo2-spi.c
index 114a64d2b7a4..0137c1e2e706 100644
--- a/drivers/fpga/machxo2-spi.c
+++ b/drivers/fpga/machxo2-spi.c
@@ -366,12 +366,12 @@ static int machxo2_spi_probe(struct spi_device *spi)
 		return -EINVAL;
 	}
 
-	mgr = devm_fpga_mgr_create(dev, "Lattice MachXO2 SPI FPGA Manager",
-				   &machxo2_ops, spi);
-	if (!mgr)
-		return -ENOMEM;
+	mgr = devm_fpga_mgr_register(dev, "Lattice MachXO2 SPI FPGA Manager",
+				     &machxo2_ops, spi);
+	if (IS_ERR(mgr))
+		return PTR_ERR(mgr);
 
-	return devm_fpga_mgr_register(dev, mgr);
+	return 0;
 }
 
 static const struct of_device_id of_match[] = {
diff --git a/drivers/fpga/socfpga-a10.c b/drivers/fpga/socfpga-a10.c
index 573d88bdf730..ac8e89b8a5cc 100644
--- a/drivers/fpga/socfpga-a10.c
+++ b/drivers/fpga/socfpga-a10.c
@@ -508,19 +508,15 @@ static int socfpga_a10_fpga_probe(struct platform_device *pdev)
 		return -EBUSY;
 	}
 
-	mgr = devm_fpga_mgr_create(dev, "SoCFPGA Arria10 FPGA Manager",
-				   &socfpga_a10_fpga_mgr_ops, priv);
-	if (!mgr)
-		return -ENOMEM;
-
-	platform_set_drvdata(pdev, mgr);
-
-	ret = fpga_mgr_register(mgr);
-	if (ret) {
+	mgr = fpga_mgr_register(dev, "SoCFPGA Arria10 FPGA Manager",
+				&socfpga_a10_fpga_mgr_ops, priv);
+	if (IS_ERR(mgr)) {
 		clk_disable_unprepare(priv->clk);
-		return ret;
+		return PTR_ERR(mgr);
 	}
 
+	platform_set_drvdata(pdev, mgr);
+
 	return 0;
 }
 
diff --git a/drivers/fpga/socfpga.c b/drivers/fpga/socfpga.c
index 1f467173fc1f..ebfa9f4d24ef 100644
--- a/drivers/fpga/socfpga.c
+++ b/drivers/fpga/socfpga.c
@@ -571,12 +571,12 @@ static int socfpga_fpga_probe(struct platform_device *pdev)
 	if (ret)
 		return ret;
 
-	mgr = devm_fpga_mgr_create(dev, "Altera SOCFPGA FPGA Manager",
-				   &socfpga_fpga_ops, priv);
-	if (!mgr)
-		return -ENOMEM;
+	mgr = devm_fpga_mgr_register(dev, "Altera SOCFPGA FPGA Manager",
+				     &socfpga_fpga_ops, priv);
+	if (IS_ERR(mgr))
+		return PTR_ERR(mgr);
 
-	return devm_fpga_mgr_register(dev, mgr);
+	return 0;
 }
 
 #ifdef CONFIG_OF
diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c
index a2cea500f7cc..9155e888a133 100644
--- a/drivers/fpga/stratix10-soc.c
+++ b/drivers/fpga/stratix10-soc.c
@@ -425,18 +425,11 @@ static int s10_probe(struct platform_device *pdev)
 
 	init_completion(&priv->status_return_completion);
 
-	mgr = fpga_mgr_create(dev, "Stratix10 SOC FPGA Manager",
-			      &s10_ops, priv);
-	if (!mgr) {
-		dev_err(dev, "unable to create FPGA manager\n");
-		ret = -ENOMEM;
-		goto probe_err;
-	}
-
-	ret = fpga_mgr_register(mgr);
-	if (ret) {
+	mgr = fpga_mgr_register(dev, "Stratix10 SOC FPGA Manager",
+				&s10_ops, priv);
+	if (IS_ERR(mgr)) {
 		dev_err(dev, "unable to register FPGA manager\n");
-		fpga_mgr_free(mgr);
+		ret = PTR_ERR(mgr);
 		goto probe_err;
 	}
 
@@ -454,7 +447,6 @@ static int s10_remove(struct platform_device *pdev)
 	struct s10_priv *priv = mgr->priv;
 
 	fpga_mgr_unregister(mgr);
-	fpga_mgr_free(mgr);
 	stratix10_svc_free_channel(priv->chan);
 
 	return 0;
diff --git a/drivers/fpga/ts73xx-fpga.c b/drivers/fpga/ts73xx-fpga.c
index 101f016c6ed8..5efbec2b9f9f 100644
--- a/drivers/fpga/ts73xx-fpga.c
+++ b/drivers/fpga/ts73xx-fpga.c
@@ -122,12 +122,12 @@ static int ts73xx_fpga_probe(struct platform_device *pdev)
 	if (IS_ERR(priv->io_base))
 		return PTR_ERR(priv->io_base);
 
-	mgr = devm_fpga_mgr_create(kdev, "TS-73xx FPGA Manager",
-				   &ts73xx_fpga_ops, priv);
-	if (!mgr)
-		return -ENOMEM;
+	mgr = devm_fpga_mgr_register(kdev, "TS-73xx FPGA Manager",
+				     &ts73xx_fpga_ops, priv);
+	if (IS_ERR(mgr))
+		return PTR_ERR(mgr);
 
-	return devm_fpga_mgr_register(kdev, mgr);
+	return 0;
 }
 
 static struct platform_driver ts73xx_fpga_driver = {
diff --git a/drivers/fpga/xilinx-spi.c b/drivers/fpga/xilinx-spi.c
index fee4d0abf6bf..efda475f7a98 100644
--- a/drivers/fpga/xilinx-spi.c
+++ b/drivers/fpga/xilinx-spi.c
@@ -247,13 +247,13 @@ static int xilinx_spi_probe(struct spi_device *spi)
 		return dev_err_probe(&spi->dev, PTR_ERR(conf->done),
 				     "Failed to get DONE gpio\n");
 
-	mgr = devm_fpga_mgr_create(&spi->dev,
-				   "Xilinx Slave Serial FPGA Manager",
-				   &xilinx_spi_ops, conf);
-	if (!mgr)
-		return -ENOMEM;
+	mgr = devm_fpga_mgr_register(&spi->dev,
+				     "Xilinx Slave Serial FPGA Manager",
+				     &xilinx_spi_ops, conf);
+	if (IS_ERR(mgr))
+		return PTR_ERR(mgr);
 
-	return devm_fpga_mgr_register(&spi->dev, mgr);
+	return 0;
 }
 
 static const struct of_device_id xlnx_spi_of_match[] = {
diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c
index 9b75bd4f93d8..426aa34c6a0d 100644
--- a/drivers/fpga/zynq-fpga.c
+++ b/drivers/fpga/zynq-fpga.c
@@ -609,20 +609,16 @@ static int zynq_fpga_probe(struct platform_device *pdev)
 
 	clk_disable(priv->clk);
 
-	mgr = devm_fpga_mgr_create(dev, "Xilinx Zynq FPGA Manager",
-				   &zynq_fpga_ops, priv);
-	if (!mgr)
-		return -ENOMEM;
-
-	platform_set_drvdata(pdev, mgr);
-
-	err = fpga_mgr_register(mgr);
-	if (err) {
+	mgr = fpga_mgr_register(dev, "Xilinx Zynq FPGA Manager",
+				&zynq_fpga_ops, priv);
+	if (IS_ERR(mgr)) {
 		dev_err(dev, "unable to register FPGA manager\n");
 		clk_unprepare(priv->clk);
-		return err;
+		return PTR_ERR(mgr);
 	}
 
+	platform_set_drvdata(pdev, mgr);
+
 	return 0;
 }
 
diff --git a/drivers/fpga/zynqmp-fpga.c b/drivers/fpga/zynqmp-fpga.c
index 125743c9797f..1a6d520985be 100644
--- a/drivers/fpga/zynqmp-fpga.c
+++ b/drivers/fpga/zynqmp-fpga.c
@@ -102,12 +102,12 @@ static int zynqmp_fpga_probe(struct platform_device *pdev)
 
 	priv->dev = dev;
 
-	mgr = devm_fpga_mgr_create(dev, "Xilinx ZynqMP FPGA Manager",
-				   &zynqmp_fpga_ops, priv);
-	if (!mgr)
-		return -ENOMEM;
+	mgr = devm_fpga_mgr_register(dev, "Xilinx ZynqMP FPGA Manager",
+				     &zynqmp_fpga_ops, priv);
+	if (IS_ERR(mgr))
+		return PTR_ERR(mgr);
 
-	return devm_fpga_mgr_register(dev, mgr);
+	return 0;
 }
 
 static const struct of_device_id zynqmp_fpga_of_match[] = {
diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
index 474c1f506307..d3c6f0153457 100644
--- a/include/linux/fpga/fpga-mgr.h
+++ b/include/linux/fpga/fpga-mgr.h
@@ -105,6 +105,17 @@ struct fpga_image_info {
 #endif
 };
 
+/**
+ * struct fpga_compat_id - id for compatibility check
+ *
+ * @id_h: high 64bit of the compat_id
+ * @id_l: low 64bit of the compat_id
+ */
+struct fpga_compat_id {
+	u64 id_h;
+	u64 id_l;
+};
+
 /**
  * struct fpga_manager_ops - ops for low level fpga manager drivers
  * @initial_header_size: Maximum number of bytes that should be passed into write_init
@@ -116,6 +127,7 @@ struct fpga_image_info {
  * @write_complete: set FPGA to operating state after writing is done
  * @fpga_remove: optional: Set FPGA into a specific state during driver remove
  * @groups: optional attribute groups.
+ * @compat_id: optional: FPGA manager id for compatibility check.
  *
  * fpga_manager_ops are the low level functions implemented by a specific
  * fpga manager driver.  The optional ones are tested for NULL before being
@@ -134,6 +146,7 @@ struct fpga_manager_ops {
 			      struct fpga_image_info *info);
 	void (*fpga_remove)(struct fpga_manager *mgr);
 	const struct attribute_group **groups;
+	struct fpga_compat_id *compat_id;
 };
 
 /* FPGA manager status: Partial/Full Reconfiguration errors */
@@ -143,24 +156,12 @@ struct fpga_manager_ops {
 #define FPGA_MGR_STATUS_IP_PROTOCOL_ERR		BIT(3)
 #define FPGA_MGR_STATUS_FIFO_OVERFLOW_ERR	BIT(4)
 
-/**
- * struct fpga_compat_id - id for compatibility check
- *
- * @id_h: high 64bit of the compat_id
- * @id_l: low 64bit of the compat_id
- */
-struct fpga_compat_id {
-	u64 id_h;
-	u64 id_l;
-};
-
 /**
  * struct fpga_manager - fpga manager structure
  * @name: name of low level fpga manager
  * @dev: fpga manager device
  * @ref_mutex: only allows one reference to fpga manager
  * @state: state of fpga manager
- * @compat_id: FPGA manager id for compatibility check.
  * @mops: pointer to struct of fpga manager ops
  * @priv: low level driver private date
  */
@@ -169,7 +170,6 @@ struct fpga_manager {
 	struct device dev;
 	struct mutex ref_mutex;
 	enum fpga_mgr_states state;
-	struct fpga_compat_id *compat_id;
 	const struct fpga_manager_ops *mops;
 	void *priv;
 };
@@ -191,17 +191,14 @@ struct fpga_manager *fpga_mgr_get(struct device *dev);
 
 void fpga_mgr_put(struct fpga_manager *mgr);
 
-struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
-				     const struct fpga_manager_ops *mops,
-				     void *priv);
-void fpga_mgr_free(struct fpga_manager *mgr);
-int fpga_mgr_register(struct fpga_manager *mgr);
+struct fpga_manager *fpga_mgr_register(struct device *dev, const char *name,
+				       const struct fpga_manager_ops *mops,
+				       void *priv);
 void fpga_mgr_unregister(struct fpga_manager *mgr);
 
-int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr);
-
-struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name,
-					  const struct fpga_manager_ops *mops,
-					  void *priv);
+struct fpga_manager *
+devm_fpga_mgr_register(struct device *dev, const char *name,
+		       const struct fpga_manager_ops *mops,
+		       void *priv);
 
 #endif /*_LINUX_FPGA_MGR_H */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v5 2/3] fpga: bridge: Use standard dev_release for class driver
  2021-06-16 22:57 [PATCH v5 0/3] fpga: Use standard class dev_release function Russ Weight
  2021-06-16 22:57 ` [PATCH v5 1/3] fpga: mgr: Use standard dev_release for class driver Russ Weight
@ 2021-06-16 22:57 ` Russ Weight
  2021-06-18 15:52   ` Xu Yilun
  2021-06-16 22:57 ` [PATCH v5 3/3] fpga: region: " Russ Weight
  2 siblings, 1 reply; 19+ messages in thread
From: Russ Weight @ 2021-06-16 22:57 UTC (permalink / raw)
  To: mdf, linux-fpga
  Cc: trix, lgoncalv, yilun.xu, hao.wu, matthew.gerlach, richard.gong,
	Russ Weight

The FPGA bridge class driver data structure is being treated as a
managed resource instead of using standard dev_release call-back
to release the class data structure. This change removes the
managed resource code and combines the create() and register()
functions into a single register() function.

Signed-off-by: Russ Weight <russell.h.weight@intel.com>
---
v5:
  - Rebased on top of recently accepted patches.
v4:
  - Restore the previous format for the Return value in the comment header
    for fpga_bridge_register()
v3:
  - Cleaned up comment header for fpga_bridge_register()
  - Fix error return values for fpga_bridge_register()
v2:
  - No changes
---
 drivers/fpga/altera-fpga2sdram.c    |  12 +--
 drivers/fpga/altera-freeze-bridge.c |  10 +--
 drivers/fpga/altera-hps2fpga.c      |  12 +--
 drivers/fpga/dfl-fme-br.c           |  10 +--
 drivers/fpga/fpga-bridge.c          | 116 ++++++----------------------
 drivers/fpga/xilinx-pr-decoupler.c  |  17 ++--
 include/linux/fpga/fpga-bridge.h    |  12 +--
 7 files changed, 49 insertions(+), 140 deletions(-)

diff --git a/drivers/fpga/altera-fpga2sdram.c b/drivers/fpga/altera-fpga2sdram.c
index a78e49c63c64..66063507116b 100644
--- a/drivers/fpga/altera-fpga2sdram.c
+++ b/drivers/fpga/altera-fpga2sdram.c
@@ -121,17 +121,13 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev)
 	/* Get f2s bridge configuration saved in handoff register */
 	regmap_read(sysmgr, SYSMGR_ISWGRP_HANDOFF3, &priv->mask);
 
-	br = devm_fpga_bridge_create(dev, F2S_BRIDGE_NAME,
-				     &altera_fpga2sdram_br_ops, priv);
-	if (!br)
-		return -ENOMEM;
+	br = fpga_bridge_register(dev, F2S_BRIDGE_NAME,
+				  &altera_fpga2sdram_br_ops, priv);
+	if (IS_ERR(br))
+		return PTR_ERR(mgr);
 
 	platform_set_drvdata(pdev, br);
 
-	ret = fpga_bridge_register(br);
-	if (ret)
-		return ret;
-
 	dev_info(dev, "driver initialized with handoff %08x\n", priv->mask);
 
 	if (!of_property_read_u32(dev->of_node, "bridge-enable", &enable)) {
diff --git a/drivers/fpga/altera-freeze-bridge.c b/drivers/fpga/altera-freeze-bridge.c
index dd58c4aea92e..bfbfa43cd05b 100644
--- a/drivers/fpga/altera-freeze-bridge.c
+++ b/drivers/fpga/altera-freeze-bridge.c
@@ -244,14 +244,14 @@ static int altera_freeze_br_probe(struct platform_device *pdev)
 
 	priv->base_addr = base_addr;
 
-	br = devm_fpga_bridge_create(dev, FREEZE_BRIDGE_NAME,
-				     &altera_freeze_br_br_ops, priv);
-	if (!br)
-		return -ENOMEM;
+	br = fpga_bridge_register(dev, FREEZE_BRIDGE_NAME,
+				  &altera_freeze_br_br_ops, priv);
+	if (IS_ERR(br))
+		return PTR_ERR(br);
 
 	platform_set_drvdata(pdev, br);
 
-	return fpga_bridge_register(br);
+	return 0;
 }
 
 static int altera_freeze_br_remove(struct platform_device *pdev)
diff --git a/drivers/fpga/altera-hps2fpga.c b/drivers/fpga/altera-hps2fpga.c
index 77b95f251821..aa758426c22b 100644
--- a/drivers/fpga/altera-hps2fpga.c
+++ b/drivers/fpga/altera-hps2fpga.c
@@ -180,19 +180,15 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev)
 		}
 	}
 
-	br = devm_fpga_bridge_create(dev, priv->name,
-				     &altera_hps2fpga_br_ops, priv);
-	if (!br) {
-		ret = -ENOMEM;
+	br = fpga_bridge_register(dev, priv->name,
+				  &altera_hps2fpga_br_ops, priv);
+	if (IS_ERR(br)) {
+		ret = PTR_ERR(br);
 		goto err;
 	}
 
 	platform_set_drvdata(pdev, br);
 
-	ret = fpga_bridge_register(br);
-	if (ret)
-		goto err;
-
 	return 0;
 
 err:
diff --git a/drivers/fpga/dfl-fme-br.c b/drivers/fpga/dfl-fme-br.c
index 3ff9f3a687ce..808d1f4d76df 100644
--- a/drivers/fpga/dfl-fme-br.c
+++ b/drivers/fpga/dfl-fme-br.c
@@ -68,14 +68,14 @@ static int fme_br_probe(struct platform_device *pdev)
 
 	priv->pdata = dev_get_platdata(dev);
 
-	br = devm_fpga_bridge_create(dev, "DFL FPGA FME Bridge",
-				     &fme_bridge_ops, priv);
-	if (!br)
-		return -ENOMEM;
+	br = fpga_bridge_register(dev, "DFL FPGA FME Bridge",
+				  &fme_bridge_ops, priv);
+	if (IS_ERR(br))
+		return PTR_ERR(br);
 
 	platform_set_drvdata(pdev, br);
 
-	return fpga_bridge_register(br);
+	return 0;
 }
 
 static int fme_br_remove(struct platform_device *pdev)
diff --git a/drivers/fpga/fpga-bridge.c b/drivers/fpga/fpga-bridge.c
index 798f55670646..15be378b64ab 100644
--- a/drivers/fpga/fpga-bridge.c
+++ b/drivers/fpga/fpga-bridge.c
@@ -312,36 +312,35 @@ static struct attribute *fpga_bridge_attrs[] = {
 ATTRIBUTE_GROUPS(fpga_bridge);
 
 /**
- * fpga_bridge_create - create and initialize a struct fpga_bridge
+ * fpga_bridge_register - create and register a FPGA Bridge device
  * @parent:	FPGA bridge device from pdev
  * @name:	FPGA bridge name
  * @br_ops:	pointer to structure of fpga bridge ops
  * @priv:	FPGA bridge private data
  *
- * The caller of this function is responsible for freeing the bridge with
- * fpga_bridge_free().  Using devm_fpga_bridge_create() instead is recommended.
- *
- * Return: struct fpga_bridge or NULL
+ * Return: struct fpga_bridge pointer or ERR_PTR()
  */
-struct fpga_bridge *fpga_bridge_create(struct device *parent, const char *name,
-				       const struct fpga_bridge_ops *br_ops,
-				       void *priv)
+struct fpga_bridge *fpga_bridge_register(struct device *parent, const char *name,
+					 const struct fpga_bridge_ops *br_ops,
+					 void *priv)
 {
 	struct fpga_bridge *bridge;
 	int id, ret;
 
 	if (!name || !strlen(name)) {
 		dev_err(parent, "Attempt to register with no name!\n");
-		return NULL;
+		return ERR_PTR(-EINVAL);
 	}
 
 	bridge = kzalloc(sizeof(*bridge), GFP_KERNEL);
 	if (!bridge)
-		return NULL;
+		return ERR_PTR(-ENOMEM);
 
 	id = ida_simple_get(&fpga_bridge_ida, 0, 0, GFP_KERNEL);
-	if (id < 0)
+	if (id < 0) {
+		ret = id;
 		goto error_kfree;
+	}
 
 	mutex_init(&bridge->mutex);
 	INIT_LIST_HEAD(&bridge->node);
@@ -350,17 +349,23 @@ struct fpga_bridge *fpga_bridge_create(struct device *parent, const char *name,
 	bridge->br_ops = br_ops;
 	bridge->priv = priv;
 
-	device_initialize(&bridge->dev);
 	bridge->dev.groups = br_ops->groups;
 	bridge->dev.class = fpga_bridge_class;
 	bridge->dev.parent = parent;
 	bridge->dev.of_node = parent->of_node;
 	bridge->dev.id = id;
+	of_platform_populate(bridge->dev.of_node, NULL, NULL, &bridge->dev);
 
 	ret = dev_set_name(&bridge->dev, "br%d", id);
 	if (ret)
 		goto error_device;
 
+	ret = device_register(&bridge->dev);
+	if (ret) {
+		put_device(&bridge->dev);
+		return ERR_PTR(ret);
+	}
+
 	return bridge;
 
 error_device:
@@ -368,88 +373,7 @@ struct fpga_bridge *fpga_bridge_create(struct device *parent, const char *name,
 error_kfree:
 	kfree(bridge);
 
-	return NULL;
-}
-EXPORT_SYMBOL_GPL(fpga_bridge_create);
-
-/**
- * fpga_bridge_free - free an fpga bridge created by fpga_bridge_create()
- * @bridge:	FPGA bridge struct
- */
-void fpga_bridge_free(struct fpga_bridge *bridge)
-{
-	ida_simple_remove(&fpga_bridge_ida, bridge->dev.id);
-	kfree(bridge);
-}
-EXPORT_SYMBOL_GPL(fpga_bridge_free);
-
-static void devm_fpga_bridge_release(struct device *dev, void *res)
-{
-	struct fpga_bridge *bridge = *(struct fpga_bridge **)res;
-
-	fpga_bridge_free(bridge);
-}
-
-/**
- * devm_fpga_bridge_create - create and init a managed struct fpga_bridge
- * @parent:	FPGA bridge device from pdev
- * @name:	FPGA bridge name
- * @br_ops:	pointer to structure of fpga bridge ops
- * @priv:	FPGA bridge private data
- *
- * This function is intended for use in an FPGA bridge driver's probe function.
- * After the bridge driver creates the struct with devm_fpga_bridge_create(), it
- * should register the bridge with fpga_bridge_register().  The bridge driver's
- * remove function should call fpga_bridge_unregister().  The bridge 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_bridge_register(), the struct will still get cleaned up.
- *
- *  Return: struct fpga_bridge or NULL
- */
-struct fpga_bridge
-*devm_fpga_bridge_create(struct device *parent, const char *name,
-			 const struct fpga_bridge_ops *br_ops, void *priv)
-{
-	struct fpga_bridge **ptr, *bridge;
-
-	ptr = devres_alloc(devm_fpga_bridge_release, sizeof(*ptr), GFP_KERNEL);
-	if (!ptr)
-		return NULL;
-
-	bridge = fpga_bridge_create(parent, name, br_ops, priv);
-	if (!bridge) {
-		devres_free(ptr);
-	} else {
-		*ptr = bridge;
-		devres_add(parent, ptr);
-	}
-
-	return bridge;
-}
-EXPORT_SYMBOL_GPL(devm_fpga_bridge_create);
-
-/**
- * fpga_bridge_register - register an FPGA bridge
- *
- * @bridge: FPGA bridge struct
- *
- * Return: 0 for success, error code otherwise.
- */
-int fpga_bridge_register(struct fpga_bridge *bridge)
-{
-	struct device *dev = &bridge->dev;
-	int ret;
-
-	ret = device_add(dev);
-	if (ret)
-		return ret;
-
-	of_platform_populate(dev->of_node, NULL, NULL, dev);
-
-	dev_info(dev->parent, "fpga bridge [%s] registered\n", bridge->name);
-
-	return 0;
+	return ERR_PTR(ret);
 }
 EXPORT_SYMBOL_GPL(fpga_bridge_register);
 
@@ -475,6 +399,10 @@ EXPORT_SYMBOL_GPL(fpga_bridge_unregister);
 
 static void fpga_bridge_dev_release(struct device *dev)
 {
+	struct fpga_bridge *bridge = to_fpga_bridge(dev);
+
+	ida_simple_remove(&fpga_bridge_ida, bridge->dev.id);
+	kfree(bridge);
 }
 
 static int __init fpga_bridge_dev_init(void)
diff --git a/drivers/fpga/xilinx-pr-decoupler.c b/drivers/fpga/xilinx-pr-decoupler.c
index ea2bde6e5bc4..c004e52b9464 100644
--- a/drivers/fpga/xilinx-pr-decoupler.c
+++ b/drivers/fpga/xilinx-pr-decoupler.c
@@ -138,22 +138,17 @@ static int xlnx_pr_decoupler_probe(struct platform_device *pdev)
 
 	clk_disable(priv->clk);
 
-	br = devm_fpga_bridge_create(&pdev->dev, priv->ipconfig->name,
-				     &xlnx_pr_decoupler_br_ops, priv);
-	if (!br) {
-		err = -ENOMEM;
-		goto err_clk;
-	}
-
-	platform_set_drvdata(pdev, br);
-
-	err = fpga_bridge_register(br);
-	if (err) {
+	br = fpga_bridge_register(&pdev->dev, priv->ipconfig->name,
+				  &xlnx_pr_decoupler_br_ops, priv);
+	if (IS_ERR(br)) {
+		err = PTR_ERR(br);
 		dev_err(&pdev->dev, "unable to register %s",
 			priv->ipconfig->name);
 		goto err_clk;
 	}
 
+	platform_set_drvdata(pdev, br);
+
 	return 0;
 
 err_clk:
diff --git a/include/linux/fpga/fpga-bridge.h b/include/linux/fpga/fpga-bridge.h
index 6c3c28806ff1..2f0eb62c49f5 100644
--- a/include/linux/fpga/fpga-bridge.h
+++ b/include/linux/fpga/fpga-bridge.h
@@ -62,15 +62,9 @@ int of_fpga_bridge_get_to_list(struct device_node *np,
 			       struct fpga_image_info *info,
 			       struct list_head *bridge_list);
 
-struct fpga_bridge *fpga_bridge_create(struct device *dev, const char *name,
-				       const struct fpga_bridge_ops *br_ops,
-				       void *priv);
-void fpga_bridge_free(struct fpga_bridge *br);
-int fpga_bridge_register(struct fpga_bridge *br);
+struct fpga_bridge *fpga_bridge_register(struct device *dev, const char *name,
+					 const struct fpga_bridge_ops *br_ops,
+					 void *priv);
 void fpga_bridge_unregister(struct fpga_bridge *br);
 
-struct fpga_bridge
-*devm_fpga_bridge_create(struct device *dev, const char *name,
-			 const struct fpga_bridge_ops *br_ops, void *priv);
-
 #endif /* _LINUX_FPGA_BRIDGE_H */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v5 3/3] fpga: region: Use standard dev_release for class driver
  2021-06-16 22:57 [PATCH v5 0/3] fpga: Use standard class dev_release function Russ Weight
  2021-06-16 22:57 ` [PATCH v5 1/3] fpga: mgr: Use standard dev_release for class driver Russ Weight
  2021-06-16 22:57 ` [PATCH v5 2/3] fpga: bridge: " Russ Weight
@ 2021-06-16 22:57 ` Russ Weight
  2021-06-18 15:54   ` Xu Yilun
  2 siblings, 1 reply; 19+ messages in thread
From: Russ Weight @ 2021-06-16 22:57 UTC (permalink / raw)
  To: mdf, linux-fpga
  Cc: trix, lgoncalv, yilun.xu, hao.wu, matthew.gerlach, richard.gong,
	Russ Weight

The FPGA region class driver data structure is being treated as a
managed resource instead of using standard dev_release call-back
to release the class data structure. This change removes the
managed resource code and combines the create() and register()
functions into a single register() function.

Signed-off-by: Russ Weight <russell.h.weight@intel.com>
---
v5:
  - Rebased on top of recently accepted patches.
  - Created the fpga_region_ops data structure which is optionally passed
    to fpga_region_register(). compat_id, the get_bridges() pointer, and
    the priv pointer are included in the fpga_region_ops structure.
v4:
  - Added the compat_id parameter to fpga_region_register() to ensure
    that the compat_id is set before the device_register() call.
  - Modified the dfl_fpga_feature_devs_enumerate() function to restore
    the fpga_region_register() call to the correct location.
v3:
  - Cleaned up comment header for fpga_region_register()
  - Fix fpga_region_register() error return on ida_simple_get() failure
v2:
  - No changes
---
 drivers/fpga/dfl-fme-pr.c        |   2 +-
 drivers/fpga/dfl-fme-region.c    |  32 ++++++---
 drivers/fpga/dfl.c               |  12 ++--
 drivers/fpga/fpga-bridge.c       |   2 +-
 drivers/fpga/fpga-region.c       | 119 ++++++++-----------------------
 drivers/fpga/of-fpga-region.c    |  14 ++--
 include/linux/fpga/fpga-region.h |  38 ++++++----
 7 files changed, 86 insertions(+), 133 deletions(-)

diff --git a/drivers/fpga/dfl-fme-pr.c b/drivers/fpga/dfl-fme-pr.c
index d61ce9a18879..4805d8c533d4 100644
--- a/drivers/fpga/dfl-fme-pr.c
+++ b/drivers/fpga/dfl-fme-pr.c
@@ -151,7 +151,7 @@ static int fme_pr(struct platform_device *pdev, unsigned long arg)
 	 * reenabling the bridge to clear things out between acceleration runs.
 	 * so no need to hold the bridges after partial reconfiguration.
 	 */
-	if (region->get_bridges)
+	if (region->rops && region->rops->get_bridges)
 		fpga_bridges_put(&region->bridge_list);
 
 	put_device(&region->dev);
diff --git a/drivers/fpga/dfl-fme-region.c b/drivers/fpga/dfl-fme-region.c
index a64c13e198a0..16d8b3f6f07a 100644
--- a/drivers/fpga/dfl-fme-region.c
+++ b/drivers/fpga/dfl-fme-region.c
@@ -21,12 +21,29 @@
 
 static int fme_region_get_bridges(struct fpga_region *region)
 {
-	struct dfl_fme_region_pdata *pdata = region->priv;
+	struct dfl_fme_region_pdata *pdata = region->rops->priv;
 	struct device *dev = &pdata->br->dev;
 
 	return fpga_bridge_get_to_list(dev, region->info, &region->bridge_list);
 }
 
+static struct fpga_region_ops *
+fme_region_get_ops(struct device *dev, struct fpga_manager *mgr,
+		   struct dfl_fme_region_pdata *pdata)
+{
+	struct fpga_region_ops *ops;
+
+	ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL);
+	if (!ops)
+		return NULL;
+
+	ops->get_bridges = fme_region_get_bridges;
+	ops->compat_id = mgr->mops->compat_id;
+	ops->priv = pdata;
+
+	return ops;
+}
+
 static int fme_region_probe(struct platform_device *pdev)
 {
 	struct dfl_fme_region_pdata *pdata = dev_get_platdata(&pdev->dev);
@@ -39,20 +56,15 @@ static int fme_region_probe(struct platform_device *pdev)
 	if (IS_ERR(mgr))
 		return -EPROBE_DEFER;
 
-	region = devm_fpga_region_create(dev, mgr, fme_region_get_bridges);
-	if (!region) {
-		ret = -ENOMEM;
+	region = fpga_region_register(dev, mgr,
+				      fme_region_get_ops(dev, mgr, pdata));
+	if (IS_ERR(region)) {
+		ret = PTR_ERR(region);
 		goto eprobe_mgr_put;
 	}
 
-	region->priv = pdata;
-	region->compat_id = mgr->mops->compat_id;
 	platform_set_drvdata(pdev, region);
 
-	ret = fpga_region_register(region);
-	if (ret)
-		goto eprobe_mgr_put;
-
 	dev_dbg(dev, "DFL FME FPGA Region probed\n");
 
 	return 0;
diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c
index 511b20ff35a3..3905995ef6d5 100644
--- a/drivers/fpga/dfl.c
+++ b/drivers/fpga/dfl.c
@@ -1400,19 +1400,15 @@ dfl_fpga_feature_devs_enumerate(struct dfl_fpga_enum_info *info)
 	if (!cdev)
 		return ERR_PTR(-ENOMEM);
 
-	cdev->region = devm_fpga_region_create(info->dev, NULL, NULL);
-	if (!cdev->region) {
-		ret = -ENOMEM;
-		goto free_cdev_exit;
-	}
-
 	cdev->parent = info->dev;
 	mutex_init(&cdev->lock);
 	INIT_LIST_HEAD(&cdev->port_dev_list);
 
-	ret = fpga_region_register(cdev->region);
-	if (ret)
+	cdev->region = fpga_region_register(info->dev, NULL, NULL);
+	if (IS_ERR(cdev->region)) {
+		ret = PTR_ERR(cdev->region);
 		goto free_cdev_exit;
+	}
 
 	/* create and init build info for enumeration */
 	binfo = devm_kzalloc(info->dev, sizeof(*binfo), GFP_KERNEL);
diff --git a/drivers/fpga/fpga-bridge.c b/drivers/fpga/fpga-bridge.c
index 15be378b64ab..f7e8500561f4 100644
--- a/drivers/fpga/fpga-bridge.c
+++ b/drivers/fpga/fpga-bridge.c
@@ -312,7 +312,7 @@ static struct attribute *fpga_bridge_attrs[] = {
 ATTRIBUTE_GROUPS(fpga_bridge);
 
 /**
- * fpga_bridge_register - create and register a FPGA Bridge device
+ * fpga_bridge_register - create and register an FPGA Bridge device
  * @parent:	FPGA bridge device from pdev
  * @name:	FPGA bridge name
  * @br_ops:	pointer to structure of fpga bridge ops
diff --git a/drivers/fpga/fpga-region.c b/drivers/fpga/fpga-region.c
index a4838715221f..7199ad93814f 100644
--- a/drivers/fpga/fpga-region.c
+++ b/drivers/fpga/fpga-region.c
@@ -115,8 +115,8 @@ int fpga_region_program_fpga(struct fpga_region *region)
 	 * In some cases, we already have a list of bridges in the
 	 * fpga region struct.  Or we don't have any bridges.
 	 */
-	if (region->get_bridges) {
-		ret = region->get_bridges(region);
+	if (region->rops && region->rops->get_bridges) {
+		ret = region->rops->get_bridges(region);
 		if (ret) {
 			dev_err(dev, "failed to get fpga region bridges\n");
 			goto err_unlock_mgr;
@@ -147,7 +147,7 @@ int fpga_region_program_fpga(struct fpga_region *region)
 	return 0;
 
 err_put_br:
-	if (region->get_bridges)
+	if (region->rops && region->rops->get_bridges)
 		fpga_bridges_put(&region->bridge_list);
 err_unlock_mgr:
 	fpga_mgr_unlock(region->mgr);
@@ -163,12 +163,12 @@ static ssize_t compat_id_show(struct device *dev,
 {
 	struct fpga_region *region = to_fpga_region(dev);
 
-	if (!region->compat_id)
+	if (!region->rops || !region->rops->compat_id)
 		return -ENOENT;
 
 	return sprintf(buf, "%016llx%016llx\n",
-		       (unsigned long long)region->compat_id->id_h,
-		       (unsigned long long)region->compat_id->id_l);
+		       (unsigned long long)region->rops->compat_id->id_h,
+		       (unsigned long long)region->rops->compat_id->id_l);
 }
 
 static DEVICE_ATTR_RO(compat_id);
@@ -180,39 +180,35 @@ static struct attribute *fpga_region_attrs[] = {
 ATTRIBUTE_GROUPS(fpga_region);
 
 /**
- * fpga_region_create - alloc and init a struct fpga_region
+ * fpga_region_register - create and register an FPGA Region device
  * @parent: device parent
  * @mgr: manager that programs this region
- * @get_bridges: optional function to get bridges to a list
+ * @rops: optional: FPGA Region ops for low level FPGA Region drivers
  *
- * The caller of this function is responsible for freeing the resulting region
- * struct with fpga_region_free().  Using devm_fpga_region_create() instead is
- * recommended.
- *
- * Return: struct fpga_region or NULL
+ * Return: struct fpga_region or ERR_PTR()
  */
-struct fpga_region
-*fpga_region_create(struct device *parent,
-		    struct fpga_manager *mgr,
-		    int (*get_bridges)(struct fpga_region *))
+struct fpga_region *
+fpga_region_register(struct device *parent, struct fpga_manager *mgr,
+		     struct fpga_region_ops *rops)
 {
 	struct fpga_region *region;
 	int id, ret = 0;
 
 	region = kzalloc(sizeof(*region), GFP_KERNEL);
 	if (!region)
-		return NULL;
+		return ERR_PTR(-ENOMEM);
 
 	id = ida_simple_get(&fpga_region_ida, 0, 0, GFP_KERNEL);
-	if (id < 0)
+	if (id < 0) {
+		ret = id;
 		goto err_free;
+	}
 
 	region->mgr = mgr;
-	region->get_bridges = get_bridges;
+	region->rops = rops;
 	mutex_init(&region->mutex);
 	INIT_LIST_HEAD(&region->bridge_list);
 
-	device_initialize(&region->dev);
 	region->dev.class = fpga_region_class;
 	region->dev.parent = parent;
 	region->dev.of_node = parent->of_node;
@@ -222,6 +218,12 @@ struct fpga_region
 	if (ret)
 		goto err_remove;
 
+	ret = device_register(&region->dev);
+	if (ret) {
+		put_device(&region->dev);
+		return ERR_PTR(ret);
+	}
+
 	return region;
 
 err_remove:
@@ -229,76 +231,7 @@ struct fpga_region
 err_free:
 	kfree(region);
 
-	return NULL;
-}
-EXPORT_SYMBOL_GPL(fpga_region_create);
-
-/**
- * fpga_region_free - free an FPGA region created by fpga_region_create()
- * @region: FPGA region
- */
-void fpga_region_free(struct fpga_region *region)
-{
-	ida_simple_remove(&fpga_region_ida, region->dev.id);
-	kfree(region);
-}
-EXPORT_SYMBOL_GPL(fpga_region_free);
-
-static void devm_fpga_region_release(struct device *dev, void *res)
-{
-	struct fpga_region *region = *(struct fpga_region **)res;
-
-	fpga_region_free(region);
-}
-
-/**
- * devm_fpga_region_create - create and initialize a managed FPGA region struct
- * @parent: device parent
- * @mgr: manager that programs this region
- * @get_bridges: optional function to get bridges to a list
- *
- * This function is intended for use in an FPGA region driver's probe function.
- * After the region driver creates the region struct with
- * devm_fpga_region_create(), it should register it with fpga_region_register().
- * The region driver's remove function should call fpga_region_unregister().
- * The region 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_region_register(), the struct will still get cleaned up.
- *
- * Return: struct fpga_region or NULL
- */
-struct fpga_region
-*devm_fpga_region_create(struct device *parent,
-			 struct fpga_manager *mgr,
-			 int (*get_bridges)(struct fpga_region *))
-{
-	struct fpga_region **ptr, *region;
-
-	ptr = devres_alloc(devm_fpga_region_release, sizeof(*ptr), GFP_KERNEL);
-	if (!ptr)
-		return NULL;
-
-	region = fpga_region_create(parent, mgr, get_bridges);
-	if (!region) {
-		devres_free(ptr);
-	} else {
-		*ptr = region;
-		devres_add(parent, ptr);
-	}
-
-	return region;
-}
-EXPORT_SYMBOL_GPL(devm_fpga_region_create);
-
-/**
- * fpga_region_register - register an FPGA region
- * @region: FPGA region
- *
- * Return: 0 or -errno
- */
-int fpga_region_register(struct fpga_region *region)
-{
-	return device_add(&region->dev);
+	return ERR_PTR(ret);
 }
 EXPORT_SYMBOL_GPL(fpga_region_register);
 
@@ -316,6 +249,10 @@ EXPORT_SYMBOL_GPL(fpga_region_unregister);
 
 static void fpga_region_dev_release(struct device *dev)
 {
+	struct fpga_region *region = to_fpga_region(dev);
+
+	ida_simple_remove(&fpga_region_ida, region->dev.id);
+	kfree(region);
 }
 
 /**
diff --git a/drivers/fpga/of-fpga-region.c b/drivers/fpga/of-fpga-region.c
index e3c25576b6b9..55b7c4ec8f6d 100644
--- a/drivers/fpga/of-fpga-region.c
+++ b/drivers/fpga/of-fpga-region.c
@@ -392,6 +392,10 @@ static struct notifier_block fpga_region_of_nb = {
 	.notifier_call = of_fpga_region_notify,
 };
 
+static struct fpga_region_ops of_fpga_region_ops = {
+	.get_bridges = of_fpga_region_get_bridges;
+};
+
 static int of_fpga_region_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -405,16 +409,12 @@ static int of_fpga_region_probe(struct platform_device *pdev)
 	if (IS_ERR(mgr))
 		return -EPROBE_DEFER;
 
-	region = devm_fpga_region_create(dev, mgr, of_fpga_region_get_bridges);
-	if (!region) {
-		ret = -ENOMEM;
+	region = fpga_region_register(dev, mgr, &of_fpga_region_ops);
+	if (IS_ERR(region)) {
+		ret = PTR_ERR(region);
 		goto eprobe_mgr_put;
 	}
 
-	ret = fpga_region_register(region);
-	if (ret)
-		goto eprobe_mgr_put;
-
 	of_platform_populate(np, fpga_region_of_match, NULL, &region->dev);
 	platform_set_drvdata(pdev, region);
 
diff --git a/include/linux/fpga/fpga-region.h b/include/linux/fpga/fpga-region.h
index 27cb706275db..02626dfd41ed 100644
--- a/include/linux/fpga/fpga-region.h
+++ b/include/linux/fpga/fpga-region.h
@@ -7,6 +7,24 @@
 #include <linux/fpga/fpga-mgr.h>
 #include <linux/fpga/fpga-bridge.h>
 
+struct fpga_region;
+
+/**
+ * struct fpga_region_ops - ops for low level fpga region drivers
+ * @get_bridges: optional function to get bridges to a list
+ * @compat_id: optional: FPGA manager id for compatibility check.
+ * @priv: optional: private data
+ *
+ * fpga_region_ops are the low level functions implemented by a specific
+ * fpga region driver.  The optional ones are tested for NULL before being
+ * referenced, so leaving them out is fine.
+ */
+struct fpga_region_ops {
+	int (*get_bridges)(struct fpga_region *region);
+	struct fpga_compat_id *compat_id;
+	void *priv;
+};
+
 /**
  * struct fpga_region - FPGA Region structure
  * @dev: FPGA Region device
@@ -14,9 +32,7 @@
  * @bridge_list: list of FPGA bridges specified in region
  * @mgr: FPGA manager
  * @info: FPGA image info
- * @compat_id: FPGA region id for compatibility check.
- * @priv: private data
- * @get_bridges: optional function to get bridges to a list
+ * @rops: optional: FPGA Region ops for low level FPGA region drivers
  */
 struct fpga_region {
 	struct device dev;
@@ -24,9 +40,7 @@ struct fpga_region {
 	struct list_head bridge_list;
 	struct fpga_manager *mgr;
 	struct fpga_image_info *info;
-	struct fpga_compat_id *compat_id;
-	void *priv;
-	int (*get_bridges)(struct fpga_region *region);
+	struct fpga_region_ops *rops;
 };
 
 #define to_fpga_region(d) container_of(d, struct fpga_region, dev)
@@ -37,15 +51,9 @@ struct fpga_region *fpga_region_class_find(
 
 int fpga_region_program_fpga(struct fpga_region *region);
 
-struct fpga_region
-*fpga_region_create(struct device *dev, struct fpga_manager *mgr,
-		    int (*get_bridges)(struct fpga_region *));
-void fpga_region_free(struct fpga_region *region);
-int fpga_region_register(struct fpga_region *region);
+struct fpga_region *
+fpga_region_register(struct device *dev, struct fpga_manager *mgr,
+		     struct fpga_region_ops *rops);
 void fpga_region_unregister(struct fpga_region *region);
 
-struct fpga_region
-*devm_fpga_region_create(struct device *dev, struct fpga_manager *mgr,
-			int (*get_bridges)(struct fpga_region *));
-
 #endif /* _FPGA_REGION_H */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH v5 1/3] fpga: mgr: Use standard dev_release for class driver
  2021-06-16 22:57 ` [PATCH v5 1/3] fpga: mgr: Use standard dev_release for class driver Russ Weight
@ 2021-06-18 15:45   ` Xu Yilun
  2021-06-18 16:03     ` Russ Weight
  0 siblings, 1 reply; 19+ messages in thread
From: Xu Yilun @ 2021-06-18 15:45 UTC (permalink / raw)
  To: Russ Weight
  Cc: mdf, linux-fpga, trix, lgoncalv, hao.wu, matthew.gerlach, richard.gong

On Wed, Jun 16, 2021 at 03:57:38PM -0700, Russ Weight wrote:
> The FPGA manager class driver data structure is being treated as a
> managed resource instead of using standard dev_release call-back
> to release the class data structure. This change removes the
> managed resource code for the freeing of the class data structure
> and combines the create() and register() functions into a single
> register() function.
> 
> The devm_fpga_mgr_register() function is retained.
> 
> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
> ---
> v5:
>   - Rebased on top of recently accepted patches.
>   - Removed compat_id from the fpga_mgr_register() parameter list
>     and added it to the fpga_manager_ops structure. This also required

My reason for this change is, we could avoid further change of the
fpga_mgr_register() API if we add new input parameters later on.

>     dynamically allocating the dfl-fme-ops structure in order to add
>     the appropriate compat_id.

But enforcing the dynamical allocation of the parameters is not prefered
to me. How about a dedicated structure that wraps all the needed
parameters:

struct fpga_mgr_info {
	const char *name;
	const struct fpga_manager_ops *mops;
	const struct fpga_compat_id *compat_id;
	const void *priv;
};

Then We can simply define a local variable of this struct for
fpga_mgr_register().

more details inline.


> v4:
>   - Added the compat_id parameter to fpga_mgr_register() and
>     devm_fpga_mgr_register() to ensure that the compat_id is set before
>     the device_register() call.
> v3:
>   - Cleaned up comment header for fpga_mgr_register()
>   - Fix error return on ida_simple_get() failure
> v2:
>   - Restored devm_fpga_mgr_register() functionality, adapted for the combined
>     create/register functionality.
>   - All previous callers of devm_fpga_mgr_register() will continue to call
>     devm_fpga_mgr_register().
>   - replaced unnecessary ternary operators in return statements with standard
>     if conditions.
> ---
>  drivers/fpga/altera-cvp.c        |  12 +--
>  drivers/fpga/altera-pr-ip-core.c |   8 +-
>  drivers/fpga/altera-ps-spi.c     |  10 +-
>  drivers/fpga/dfl-fme-mgr.c       |  52 ++++++----
>  drivers/fpga/dfl-fme-region.c    |   2 +-
>  drivers/fpga/fpga-mgr.c          | 163 ++++++++-----------------------
>  drivers/fpga/ice40-spi.c         |  10 +-
>  drivers/fpga/machxo2-spi.c       |  10 +-
>  drivers/fpga/socfpga-a10.c       |  16 ++-
>  drivers/fpga/socfpga.c           |  10 +-
>  drivers/fpga/stratix10-soc.c     |  16 +--
>  drivers/fpga/ts73xx-fpga.c       |  10 +-
>  drivers/fpga/xilinx-spi.c        |  12 +--
>  drivers/fpga/zynq-fpga.c         |  16 ++-
>  drivers/fpga/zynqmp-fpga.c       |  10 +-
>  include/linux/fpga/fpga-mgr.h    |  43 ++++----
>  16 files changed, 153 insertions(+), 247 deletions(-)
> 
> diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
> index ccf4546eff29..4ffb9da537d8 100644
> --- a/drivers/fpga/altera-cvp.c
> +++ b/drivers/fpga/altera-cvp.c
> @@ -652,19 +652,15 @@ static int altera_cvp_probe(struct pci_dev *pdev,
>  	snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s @%s",
>  		 ALTERA_CVP_MGR_NAME, pci_name(pdev));
>  
> -	mgr = devm_fpga_mgr_create(&pdev->dev, conf->mgr_name,
> -				   &altera_cvp_ops, conf);
> -	if (!mgr) {
> -		ret = -ENOMEM;
> +	mgr = fpga_mgr_register(&pdev->dev, conf->mgr_name,
> +				&altera_cvp_ops, conf);
> +	if (IS_ERR(mgr)) {
> +		ret = PTR_ERR(mgr);
>  		goto err_unmap;
>  	}
>  
>  	pci_set_drvdata(pdev, mgr);
>  
> -	ret = fpga_mgr_register(mgr);
> -	if (ret)
> -		goto err_unmap;
> -
>  	return 0;
>  
>  err_unmap:
> diff --git a/drivers/fpga/altera-pr-ip-core.c b/drivers/fpga/altera-pr-ip-core.c
> index dfdf21ed34c4..17babf974852 100644
> --- a/drivers/fpga/altera-pr-ip-core.c
> +++ b/drivers/fpga/altera-pr-ip-core.c
> @@ -191,11 +191,11 @@ int alt_pr_register(struct device *dev, void __iomem *reg_base)
>  		(val & ALT_PR_CSR_STATUS_MSK) >> ALT_PR_CSR_STATUS_SFT,
>  		(int)(val & ALT_PR_CSR_PR_START));
>  
> -	mgr = devm_fpga_mgr_create(dev, dev_name(dev), &alt_pr_ops, priv);
> -	if (!mgr)
> -		return -ENOMEM;
> +	mgr = devm_fpga_mgr_register(dev, dev_name(dev), &alt_pr_ops, priv);
> +	if (IS_ERR(mgr))
> +		return PTR_ERR(mgr);
>  
> -	return devm_fpga_mgr_register(dev, mgr);
> +	return 0;
>  }
>  EXPORT_SYMBOL_GPL(alt_pr_register);
>  
> diff --git a/drivers/fpga/altera-ps-spi.c b/drivers/fpga/altera-ps-spi.c
> index 23bfd4d1ad0f..d3f77b0312b2 100644
> --- a/drivers/fpga/altera-ps-spi.c
> +++ b/drivers/fpga/altera-ps-spi.c
> @@ -302,12 +302,12 @@ static int altera_ps_probe(struct spi_device *spi)
>  	snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s %s",
>  		 dev_driver_string(&spi->dev), dev_name(&spi->dev));
>  
> -	mgr = devm_fpga_mgr_create(&spi->dev, conf->mgr_name,
> -				   &altera_ps_ops, conf);
> -	if (!mgr)
> -		return -ENOMEM;
> +	mgr = devm_fpga_mgr_register(&spi->dev, conf->mgr_name,
> +				     &altera_ps_ops, conf);
> +	if (IS_ERR(mgr))
> +		return PTR_ERR(mgr);
>  
> -	return devm_fpga_mgr_register(&spi->dev, mgr);
> +	return 0;
>  }
>  
>  static const struct spi_device_id altera_ps_spi_ids[] = {
> diff --git a/drivers/fpga/dfl-fme-mgr.c b/drivers/fpga/dfl-fme-mgr.c
> index d5861d13b306..1b93bc292dbe 100644
> --- a/drivers/fpga/dfl-fme-mgr.c
> +++ b/drivers/fpga/dfl-fme-mgr.c
> @@ -264,14 +264,6 @@ static u64 fme_mgr_status(struct fpga_manager *mgr)
>  	return pr_error_to_mgr_status(priv->pr_error);
>  }
>  
> -static const struct fpga_manager_ops fme_mgr_ops = {
> -	.write_init = fme_mgr_write_init,
> -	.write = fme_mgr_write,
> -	.write_complete = fme_mgr_write_complete,
> -	.state = fme_mgr_state,
> -	.status = fme_mgr_status,
> -};
> -
>  static void fme_mgr_get_compat_id(void __iomem *fme_pr,
>  				  struct fpga_compat_id *id)
>  {
> @@ -279,10 +271,34 @@ static void fme_mgr_get_compat_id(void __iomem *fme_pr,
>  	id->id_h = readq(fme_pr + FME_PR_INTFC_ID_H);
>  }
>  
> +static struct fpga_manager_ops *fme_mgr_get_ops(struct device *dev,
> +						struct fme_mgr_priv *priv)
> +{
> +	struct fpga_manager_ops *ops;
> +
> +	ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL);
> +	if (!ops)
> +		return NULL;
> +
> +	ops->compat_id = devm_kzalloc(dev, sizeof(struct fpga_compat_id),
> +				      GFP_KERNEL);
> +	if (!ops->compat_id)
> +		return NULL;
> +
> +	fme_mgr_get_compat_id(priv->ioaddr, ops->compat_id);
> +	ops->write_init = fme_mgr_write_init;
> +	ops->write = fme_mgr_write;
> +	ops->write_complete = fme_mgr_write_complete;
> +	ops->state = fme_mgr_state;
> +	ops->status = fme_mgr_status;
> +
> +	return ops;
> +}
> +
>  static int fme_mgr_probe(struct platform_device *pdev)
>  {
>  	struct dfl_fme_mgr_pdata *pdata = dev_get_platdata(&pdev->dev);
> -	struct fpga_compat_id *compat_id;
> +	struct fpga_manager_ops *fme_mgr_ops;
>  	struct device *dev = &pdev->dev;
>  	struct fme_mgr_priv *priv;
>  	struct fpga_manager *mgr;
> @@ -302,20 +318,16 @@ static int fme_mgr_probe(struct platform_device *pdev)
>  			return PTR_ERR(priv->ioaddr);
>  	}
>  
> -	compat_id = devm_kzalloc(dev, sizeof(*compat_id), GFP_KERNEL);
> -	if (!compat_id)
> -		return -ENOMEM;
> -
> -	fme_mgr_get_compat_id(priv->ioaddr, compat_id);
> -
> -	mgr = devm_fpga_mgr_create(dev, "DFL FME FPGA Manager",
> -				   &fme_mgr_ops, priv);
> -	if (!mgr)
> +	fme_mgr_ops = fme_mgr_get_ops(dev, priv);
> +	if (!fme_mgr_ops)
>  		return -ENOMEM;
>  
> -	mgr->compat_id = compat_id;
> +	mgr = devm_fpga_mgr_register(dev, "DFL FME FPGA Manager",
> +				     fme_mgr_ops, priv);
> +	if (IS_ERR(mgr))
> +		return PTR_ERR(mgr);

With structure fpga_mgr_info, we don't need the dynamical allocation
anymore:

	struct fpga_mgr_info minfo = { 0 };

	minfo.name = "DFL FME FPGA Manager";
	minfo.mops = &fme_mgr_ops;
	minfo.compat_id = compat_id;
	minfo.priv = priv;

	mgr = devm_fpga_mgr_register(dev, &minfo);

Drivers could also leaves minfo.compat_id zero if they don't care.

I also expect more opinions.

>  
> -	return devm_fpga_mgr_register(dev, mgr);
> +	return 0;
>  }
>  
>  static struct platform_driver fme_mgr_driver = {
> diff --git a/drivers/fpga/dfl-fme-region.c b/drivers/fpga/dfl-fme-region.c
> index 1eeb42af1012..a64c13e198a0 100644
> --- a/drivers/fpga/dfl-fme-region.c
> +++ b/drivers/fpga/dfl-fme-region.c
> @@ -46,7 +46,7 @@ static int fme_region_probe(struct platform_device *pdev)
>  	}
>  
>  	region->priv = pdata;
> -	region->compat_id = mgr->compat_id;
> +	region->compat_id = mgr->mops->compat_id;

The changes should be in Patch #3, is it?

>  	platform_set_drvdata(pdev, region);
>  
>  	ret = fpga_region_register(region);
> diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
> index ecb4c3c795fa..9aecfea432c8 100644
> --- a/drivers/fpga/fpga-mgr.c
> +++ b/drivers/fpga/fpga-mgr.c
> @@ -550,20 +550,20 @@ void fpga_mgr_unlock(struct fpga_manager *mgr)
>  EXPORT_SYMBOL_GPL(fpga_mgr_unlock);
>  
>  /**
> - * fpga_mgr_create - create and initialize an FPGA manager struct
> + * 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
>   *
> - * 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 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 *parent, 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)

fpga_mgr_register(struct device *parent, struct fpga_mgr_info *minfo)

>  {
>  	struct fpga_manager *mgr;
>  	int id, ret;
> @@ -572,21 +572,23 @@ struct fpga_manager *fpga_mgr_create(struct device *parent, const char *name,
>  	    !mops->write_init || (!mops->write && !mops->write_sg) ||
>  	    (mops->write && mops->write_sg)) {
>  		dev_err(parent, "Attempt to register without fpga_manager_ops\n");
> -		return NULL;
> +		return ERR_PTR(-EINVAL);
>  	}
>  
>  	if (!name || !strlen(name)) {
>  		dev_err(parent, "Attempt to register with no name!\n");
> -		return NULL;
> +		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)
> +	if (id < 0) {
> +		ret = id;
>  		goto error_kfree;
> +	}
>  
>  	mutex_init(&mgr->ref_mutex);
>  
> @@ -594,7 +596,6 @@ struct fpga_manager *fpga_mgr_create(struct device *parent, const char *name,
>  	mgr->mops = mops;
>  	mgr->priv = priv;

	mgr->name = minfo->name;
	mgr->mops = minfo->mops;
	mgr->priv = minfo->priv;
	mgr->compat_id = minfo->compat_id;

>  
> -	device_initialize(&mgr->dev);
>  	mgr->dev.class = fpga_mgr_class;
>  	mgr->dev.groups = mops->groups;
>  	mgr->dev.parent = parent;
> @@ -605,84 +606,6 @@ struct fpga_manager *fpga_mgr_create(struct device *parent, const char *name,
>  	if (ret)
>  		goto error_device;
>  
> -	return mgr;
> -
> -error_device:
> -	ida_simple_remove(&fpga_mgr_ida, id);
> -error_kfree:
> -	kfree(mgr);
> -
> -	return NULL;
> -}
> -EXPORT_SYMBOL_GPL(fpga_mgr_create);
> -
> -/**
> - * fpga_mgr_free - free an 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_mgr_devres *dr = res;
> -
> -	fpga_mgr_free(dr->mgr);
> -}
> -
> -/**
> - * devm_fpga_mgr_create - create and initialize a managed FPGA manager struct
> - * @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 an 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.
> - *
> - * Return: pointer to struct fpga_manager or NULL
> - */
> -struct fpga_manager *devm_fpga_mgr_create(struct device *parent, const char *name,
> -					  const struct fpga_manager_ops *mops,
> -					  void *priv)
> -{
> -	struct fpga_mgr_devres *dr;
> -
> -	dr = devres_alloc(devm_fpga_mgr_release, sizeof(*dr), GFP_KERNEL);
> -	if (!dr)
> -		return NULL;
> -
> -	dr->mgr = fpga_mgr_create(parent, name, mops, priv);
> -	if (!dr->mgr) {
> -		devres_free(dr);
> -		return NULL;
> -	}
> -
> -	devres_add(parent, dr);
> -
> -	return dr->mgr;
> -}
> -EXPORT_SYMBOL_GPL(devm_fpga_mgr_create);
> -
> -/**
> - * fpga_mgr_register - register an FPGA manager
> - * @mgr: fpga manager struct
> - *
> - * Return: 0 on success, negative error code otherwise.
> - */
> -int fpga_mgr_register(struct fpga_manager *mgr)
> -{
> -	int ret;
> -
>  	/*
>  	 * Initialize framework state by requesting low level driver read state
>  	 * from device.  FPGA may be in reset mode or may have been programmed
> @@ -690,18 +613,20 @@ int fpga_mgr_register(struct fpga_manager *mgr)
>  	 */
>  	mgr->state = mgr->mops->state(mgr);
>  
> -	ret = device_add(&mgr->dev);
> -	if (ret)
> -		goto error_device;
> -
> -	dev_info(&mgr->dev, "%s registered\n", mgr->name);
> +	ret = device_register(&mgr->dev);
> +	if (ret) {
> +		put_device(&mgr->dev);
> +		return ERR_PTR(ret);
> +	}
>  
> -	return 0;
> +	return mgr;
>  
>  error_device:
> -	ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
> +	ida_simple_remove(&fpga_mgr_ida, id);
> +error_kfree:
> +	kfree(mgr);
>  
> -	return ret;
> +	return ERR_PTR(ret);
>  }
>  EXPORT_SYMBOL_GPL(fpga_mgr_register);
>  
> @@ -726,14 +651,6 @@ void fpga_mgr_unregister(struct fpga_manager *mgr)
>  }
>  EXPORT_SYMBOL_GPL(fpga_mgr_unregister);
>  
> -static int fpga_mgr_devres_match(struct device *dev, void *res,
> -				 void *match_data)
> -{
> -	struct fpga_mgr_devres *dr = res;
> -
> -	return match_data == dr->mgr;
> -}
> -
>  static void devm_fpga_mgr_unregister(struct device *dev, void *res)
>  {
>  	struct fpga_mgr_devres *dr = res;
> @@ -743,44 +660,44 @@ static void devm_fpga_mgr_unregister(struct device *dev, void *res)
>  
>  /**
>   * devm_fpga_mgr_register - resource managed variant of fpga_mgr_register()
> - * @dev: managing device for this FPGA manager
> - * @mgr: 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
>   *
>   * This is the devres variant of fpga_mgr_register() for which the unregister
>   * function will be called automatically when the managing device is detached.
>   */
> -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr)
> +struct fpga_manager *
> +devm_fpga_mgr_register(struct device *dev, const char *name,

   devm_fpga_mgr_register(struct device *parent,


> +		       const struct fpga_manager_ops *mops, void *priv)
>  {
>  	struct fpga_mgr_devres *dr;
> -	int ret;
> -
> -	/*
> -	 * Make sure that the struct fpga_manager * that is passed in is
> -	 * managed itself.
> -	 */
> -	if (WARN_ON(!devres_find(dev, devm_fpga_mgr_release,
> -				 fpga_mgr_devres_match, mgr)))
> -		return -EINVAL;
> +	struct fpga_manager *mgr;
>  
>  	dr = devres_alloc(devm_fpga_mgr_unregister, sizeof(*dr), GFP_KERNEL);
>  	if (!dr)
> -		return -ENOMEM;
> +		return ERR_PTR(-ENOMEM);
>  
> -	ret = fpga_mgr_register(mgr);
> -	if (ret) {
> +	mgr = fpga_mgr_register(dev, name, mops, priv);
> +	if (IS_ERR(mgr)) {
>  		devres_free(dr);
> -		return ret;
> +		return mgr;
>  	}
>  
>  	dr->mgr = mgr;
>  	devres_add(dev, dr);
>  
> -	return 0;
> +	return mgr;
>  }
>  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)
> diff --git a/drivers/fpga/ice40-spi.c b/drivers/fpga/ice40-spi.c
> index 69dec5af23c3..91ac25f339f3 100644
> --- a/drivers/fpga/ice40-spi.c
> +++ b/drivers/fpga/ice40-spi.c
> @@ -178,12 +178,12 @@ static int ice40_fpga_probe(struct spi_device *spi)
>  		return ret;
>  	}
>  
> -	mgr = devm_fpga_mgr_create(dev, "Lattice iCE40 FPGA Manager",
> -				   &ice40_fpga_ops, priv);
> -	if (!mgr)
> -		return -ENOMEM;
> +	mgr = devm_fpga_mgr_register(dev, "Lattice iCE40 FPGA Manager",
> +				     &ice40_fpga_ops, priv);
> +	if (IS_ERR(mgr))
> +		return PTR_ERR(mgr);
>  
> -	return devm_fpga_mgr_register(dev, mgr);
> +	return 0;
>  }
>  
>  static const struct of_device_id ice40_fpga_of_match[] = {
> diff --git a/drivers/fpga/machxo2-spi.c b/drivers/fpga/machxo2-spi.c
> index 114a64d2b7a4..0137c1e2e706 100644
> --- a/drivers/fpga/machxo2-spi.c
> +++ b/drivers/fpga/machxo2-spi.c
> @@ -366,12 +366,12 @@ static int machxo2_spi_probe(struct spi_device *spi)
>  		return -EINVAL;
>  	}
>  
> -	mgr = devm_fpga_mgr_create(dev, "Lattice MachXO2 SPI FPGA Manager",
> -				   &machxo2_ops, spi);
> -	if (!mgr)
> -		return -ENOMEM;
> +	mgr = devm_fpga_mgr_register(dev, "Lattice MachXO2 SPI FPGA Manager",
> +				     &machxo2_ops, spi);
> +	if (IS_ERR(mgr))
> +		return PTR_ERR(mgr);
>  
> -	return devm_fpga_mgr_register(dev, mgr);
> +	return 0;
>  }
>  
>  static const struct of_device_id of_match[] = {
> diff --git a/drivers/fpga/socfpga-a10.c b/drivers/fpga/socfpga-a10.c
> index 573d88bdf730..ac8e89b8a5cc 100644
> --- a/drivers/fpga/socfpga-a10.c
> +++ b/drivers/fpga/socfpga-a10.c
> @@ -508,19 +508,15 @@ static int socfpga_a10_fpga_probe(struct platform_device *pdev)
>  		return -EBUSY;
>  	}
>  
> -	mgr = devm_fpga_mgr_create(dev, "SoCFPGA Arria10 FPGA Manager",
> -				   &socfpga_a10_fpga_mgr_ops, priv);
> -	if (!mgr)
> -		return -ENOMEM;
> -
> -	platform_set_drvdata(pdev, mgr);
> -
> -	ret = fpga_mgr_register(mgr);
> -	if (ret) {
> +	mgr = fpga_mgr_register(dev, "SoCFPGA Arria10 FPGA Manager",
> +				&socfpga_a10_fpga_mgr_ops, priv);
> +	if (IS_ERR(mgr)) {
>  		clk_disable_unprepare(priv->clk);
> -		return ret;
> +		return PTR_ERR(mgr);
>  	}
>  
> +	platform_set_drvdata(pdev, mgr);
> +
>  	return 0;
>  }
>  
> diff --git a/drivers/fpga/socfpga.c b/drivers/fpga/socfpga.c
> index 1f467173fc1f..ebfa9f4d24ef 100644
> --- a/drivers/fpga/socfpga.c
> +++ b/drivers/fpga/socfpga.c
> @@ -571,12 +571,12 @@ static int socfpga_fpga_probe(struct platform_device *pdev)
>  	if (ret)
>  		return ret;
>  
> -	mgr = devm_fpga_mgr_create(dev, "Altera SOCFPGA FPGA Manager",
> -				   &socfpga_fpga_ops, priv);
> -	if (!mgr)
> -		return -ENOMEM;
> +	mgr = devm_fpga_mgr_register(dev, "Altera SOCFPGA FPGA Manager",
> +				     &socfpga_fpga_ops, priv);
> +	if (IS_ERR(mgr))
> +		return PTR_ERR(mgr);
>  
> -	return devm_fpga_mgr_register(dev, mgr);
> +	return 0;
>  }
>  
>  #ifdef CONFIG_OF
> diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c
> index a2cea500f7cc..9155e888a133 100644
> --- a/drivers/fpga/stratix10-soc.c
> +++ b/drivers/fpga/stratix10-soc.c
> @@ -425,18 +425,11 @@ static int s10_probe(struct platform_device *pdev)
>  
>  	init_completion(&priv->status_return_completion);
>  
> -	mgr = fpga_mgr_create(dev, "Stratix10 SOC FPGA Manager",
> -			      &s10_ops, priv);
> -	if (!mgr) {
> -		dev_err(dev, "unable to create FPGA manager\n");
> -		ret = -ENOMEM;
> -		goto probe_err;
> -	}
> -
> -	ret = fpga_mgr_register(mgr);
> -	if (ret) {
> +	mgr = fpga_mgr_register(dev, "Stratix10 SOC FPGA Manager",
> +				&s10_ops, priv);
> +	if (IS_ERR(mgr)) {
>  		dev_err(dev, "unable to register FPGA manager\n");
> -		fpga_mgr_free(mgr);
> +		ret = PTR_ERR(mgr);
>  		goto probe_err;
>  	}
>  
> @@ -454,7 +447,6 @@ static int s10_remove(struct platform_device *pdev)
>  	struct s10_priv *priv = mgr->priv;
>  
>  	fpga_mgr_unregister(mgr);
> -	fpga_mgr_free(mgr);
>  	stratix10_svc_free_channel(priv->chan);
>  
>  	return 0;
> diff --git a/drivers/fpga/ts73xx-fpga.c b/drivers/fpga/ts73xx-fpga.c
> index 101f016c6ed8..5efbec2b9f9f 100644
> --- a/drivers/fpga/ts73xx-fpga.c
> +++ b/drivers/fpga/ts73xx-fpga.c
> @@ -122,12 +122,12 @@ static int ts73xx_fpga_probe(struct platform_device *pdev)
>  	if (IS_ERR(priv->io_base))
>  		return PTR_ERR(priv->io_base);
>  
> -	mgr = devm_fpga_mgr_create(kdev, "TS-73xx FPGA Manager",
> -				   &ts73xx_fpga_ops, priv);
> -	if (!mgr)
> -		return -ENOMEM;
> +	mgr = devm_fpga_mgr_register(kdev, "TS-73xx FPGA Manager",
> +				     &ts73xx_fpga_ops, priv);
> +	if (IS_ERR(mgr))
> +		return PTR_ERR(mgr);
>  
> -	return devm_fpga_mgr_register(kdev, mgr);
> +	return 0;
>  }
>  
>  static struct platform_driver ts73xx_fpga_driver = {
> diff --git a/drivers/fpga/xilinx-spi.c b/drivers/fpga/xilinx-spi.c
> index fee4d0abf6bf..efda475f7a98 100644
> --- a/drivers/fpga/xilinx-spi.c
> +++ b/drivers/fpga/xilinx-spi.c
> @@ -247,13 +247,13 @@ static int xilinx_spi_probe(struct spi_device *spi)
>  		return dev_err_probe(&spi->dev, PTR_ERR(conf->done),
>  				     "Failed to get DONE gpio\n");
>  
> -	mgr = devm_fpga_mgr_create(&spi->dev,
> -				   "Xilinx Slave Serial FPGA Manager",
> -				   &xilinx_spi_ops, conf);
> -	if (!mgr)
> -		return -ENOMEM;
> +	mgr = devm_fpga_mgr_register(&spi->dev,
> +				     "Xilinx Slave Serial FPGA Manager",
> +				     &xilinx_spi_ops, conf);
> +	if (IS_ERR(mgr))
> +		return PTR_ERR(mgr);
>  
> -	return devm_fpga_mgr_register(&spi->dev, mgr);
> +	return 0;
>  }
>  
>  static const struct of_device_id xlnx_spi_of_match[] = {
> diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c
> index 9b75bd4f93d8..426aa34c6a0d 100644
> --- a/drivers/fpga/zynq-fpga.c
> +++ b/drivers/fpga/zynq-fpga.c
> @@ -609,20 +609,16 @@ static int zynq_fpga_probe(struct platform_device *pdev)
>  
>  	clk_disable(priv->clk);
>  
> -	mgr = devm_fpga_mgr_create(dev, "Xilinx Zynq FPGA Manager",
> -				   &zynq_fpga_ops, priv);
> -	if (!mgr)
> -		return -ENOMEM;
> -
> -	platform_set_drvdata(pdev, mgr);
> -
> -	err = fpga_mgr_register(mgr);
> -	if (err) {
> +	mgr = fpga_mgr_register(dev, "Xilinx Zynq FPGA Manager",
> +				&zynq_fpga_ops, priv);
> +	if (IS_ERR(mgr)) {
>  		dev_err(dev, "unable to register FPGA manager\n");
>  		clk_unprepare(priv->clk);
> -		return err;
> +		return PTR_ERR(mgr);
>  	}
>  
> +	platform_set_drvdata(pdev, mgr);
> +
>  	return 0;
>  }
>  
> diff --git a/drivers/fpga/zynqmp-fpga.c b/drivers/fpga/zynqmp-fpga.c
> index 125743c9797f..1a6d520985be 100644
> --- a/drivers/fpga/zynqmp-fpga.c
> +++ b/drivers/fpga/zynqmp-fpga.c
> @@ -102,12 +102,12 @@ static int zynqmp_fpga_probe(struct platform_device *pdev)
>  
>  	priv->dev = dev;
>  
> -	mgr = devm_fpga_mgr_create(dev, "Xilinx ZynqMP FPGA Manager",
> -				   &zynqmp_fpga_ops, priv);
> -	if (!mgr)
> -		return -ENOMEM;
> +	mgr = devm_fpga_mgr_register(dev, "Xilinx ZynqMP FPGA Manager",
> +				     &zynqmp_fpga_ops, priv);
> +	if (IS_ERR(mgr))
> +		return PTR_ERR(mgr);
>  
> -	return devm_fpga_mgr_register(dev, mgr);
> +	return 0;
>  }
>  
>  static const struct of_device_id zynqmp_fpga_of_match[] = {
> diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
> index 474c1f506307..d3c6f0153457 100644
> --- a/include/linux/fpga/fpga-mgr.h
> +++ b/include/linux/fpga/fpga-mgr.h
> @@ -105,6 +105,17 @@ struct fpga_image_info {
>  #endif
>  };
>  
> +/**
> + * struct fpga_compat_id - id for compatibility check
> + *
> + * @id_h: high 64bit of the compat_id
> + * @id_l: low 64bit of the compat_id
> + */
> +struct fpga_compat_id {
> +	u64 id_h;
> +	u64 id_l;
> +};
> +
>  /**
>   * struct fpga_manager_ops - ops for low level fpga manager drivers
>   * @initial_header_size: Maximum number of bytes that should be passed into write_init
> @@ -116,6 +127,7 @@ struct fpga_image_info {
>   * @write_complete: set FPGA to operating state after writing is done
>   * @fpga_remove: optional: Set FPGA into a specific state during driver remove
>   * @groups: optional attribute groups.
> + * @compat_id: optional: FPGA manager id for compatibility check.
>   *
>   * fpga_manager_ops are the low level functions implemented by a specific
>   * fpga manager driver.  The optional ones are tested for NULL before being
> @@ -134,6 +146,7 @@ struct fpga_manager_ops {
>  			      struct fpga_image_info *info);
>  	void (*fpga_remove)(struct fpga_manager *mgr);
>  	const struct attribute_group **groups;
> +	struct fpga_compat_id *compat_id;

We don't have to move compat_id here, keep it in struct fpga_mgr

Thanks,
Yilun

>  };
>  
>  /* FPGA manager status: Partial/Full Reconfiguration errors */
> @@ -143,24 +156,12 @@ struct fpga_manager_ops {
>  #define FPGA_MGR_STATUS_IP_PROTOCOL_ERR		BIT(3)
>  #define FPGA_MGR_STATUS_FIFO_OVERFLOW_ERR	BIT(4)
>  
> -/**
> - * struct fpga_compat_id - id for compatibility check
> - *
> - * @id_h: high 64bit of the compat_id
> - * @id_l: low 64bit of the compat_id
> - */
> -struct fpga_compat_id {
> -	u64 id_h;
> -	u64 id_l;
> -};
> -
>  /**
>   * struct fpga_manager - fpga manager structure
>   * @name: name of low level fpga manager
>   * @dev: fpga manager device
>   * @ref_mutex: only allows one reference to fpga manager
>   * @state: state of fpga manager
> - * @compat_id: FPGA manager id for compatibility check.
>   * @mops: pointer to struct of fpga manager ops
>   * @priv: low level driver private date
>   */
> @@ -169,7 +170,6 @@ struct fpga_manager {
>  	struct device dev;
>  	struct mutex ref_mutex;
>  	enum fpga_mgr_states state;
> -	struct fpga_compat_id *compat_id;
>  	const struct fpga_manager_ops *mops;
>  	void *priv;
>  };
> @@ -191,17 +191,14 @@ struct fpga_manager *fpga_mgr_get(struct device *dev);
>  
>  void fpga_mgr_put(struct fpga_manager *mgr);
>  
> -struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
> -				     const struct fpga_manager_ops *mops,
> -				     void *priv);
> -void fpga_mgr_free(struct fpga_manager *mgr);
> -int fpga_mgr_register(struct fpga_manager *mgr);
> +struct fpga_manager *fpga_mgr_register(struct device *dev, const char *name,
> +				       const struct fpga_manager_ops *mops,
> +				       void *priv);
>  void fpga_mgr_unregister(struct fpga_manager *mgr);
>  
> -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr);
> -
> -struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name,
> -					  const struct fpga_manager_ops *mops,
> -					  void *priv);
> +struct fpga_manager *
> +devm_fpga_mgr_register(struct device *dev, const char *name,
> +		       const struct fpga_manager_ops *mops,
> +		       void *priv);
>  
>  #endif /*_LINUX_FPGA_MGR_H */
> -- 
> 2.25.1

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v5 2/3] fpga: bridge: Use standard dev_release for class driver
  2021-06-16 22:57 ` [PATCH v5 2/3] fpga: bridge: " Russ Weight
@ 2021-06-18 15:52   ` Xu Yilun
  2021-06-18 16:05     ` Russ Weight
  0 siblings, 1 reply; 19+ messages in thread
From: Xu Yilun @ 2021-06-18 15:52 UTC (permalink / raw)
  To: Russ Weight
  Cc: mdf, linux-fpga, trix, lgoncalv, hao.wu, matthew.gerlach, richard.gong

On Wed, Jun 16, 2021 at 03:57:39PM -0700, Russ Weight wrote:
> The FPGA bridge class driver data structure is being treated as a
> managed resource instead of using standard dev_release call-back
> to release the class data structure. This change removes the
> managed resource code and combines the create() and register()
> functions into a single register() function.
> 
> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
> ---
> v5:
>   - Rebased on top of recently accepted patches.
> v4:
>   - Restore the previous format for the Return value in the comment header
>     for fpga_bridge_register()
> v3:
>   - Cleaned up comment header for fpga_bridge_register()
>   - Fix error return values for fpga_bridge_register()
> v2:
>   - No changes
> ---
>  drivers/fpga/altera-fpga2sdram.c    |  12 +--
>  drivers/fpga/altera-freeze-bridge.c |  10 +--
>  drivers/fpga/altera-hps2fpga.c      |  12 +--
>  drivers/fpga/dfl-fme-br.c           |  10 +--
>  drivers/fpga/fpga-bridge.c          | 116 ++++++----------------------
>  drivers/fpga/xilinx-pr-decoupler.c  |  17 ++--
>  include/linux/fpga/fpga-bridge.h    |  12 +--
>  7 files changed, 49 insertions(+), 140 deletions(-)
> 
> diff --git a/drivers/fpga/altera-fpga2sdram.c b/drivers/fpga/altera-fpga2sdram.c
> index a78e49c63c64..66063507116b 100644
> --- a/drivers/fpga/altera-fpga2sdram.c
> +++ b/drivers/fpga/altera-fpga2sdram.c
> @@ -121,17 +121,13 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev)
>  	/* Get f2s bridge configuration saved in handoff register */
>  	regmap_read(sysmgr, SYSMGR_ISWGRP_HANDOFF3, &priv->mask);
>  
> -	br = devm_fpga_bridge_create(dev, F2S_BRIDGE_NAME,
> -				     &altera_fpga2sdram_br_ops, priv);
> -	if (!br)
> -		return -ENOMEM;
> +	br = fpga_bridge_register(dev, F2S_BRIDGE_NAME,
> +				  &altera_fpga2sdram_br_ops, priv);
> +	if (IS_ERR(br))
> +		return PTR_ERR(mgr);
>  
>  	platform_set_drvdata(pdev, br);
>  
> -	ret = fpga_bridge_register(br);
> -	if (ret)
> -		return ret;
> -
>  	dev_info(dev, "driver initialized with handoff %08x\n", priv->mask);
>  
>  	if (!of_property_read_u32(dev->of_node, "bridge-enable", &enable)) {
> diff --git a/drivers/fpga/altera-freeze-bridge.c b/drivers/fpga/altera-freeze-bridge.c
> index dd58c4aea92e..bfbfa43cd05b 100644
> --- a/drivers/fpga/altera-freeze-bridge.c
> +++ b/drivers/fpga/altera-freeze-bridge.c
> @@ -244,14 +244,14 @@ static int altera_freeze_br_probe(struct platform_device *pdev)
>  
>  	priv->base_addr = base_addr;
>  
> -	br = devm_fpga_bridge_create(dev, FREEZE_BRIDGE_NAME,
> -				     &altera_freeze_br_br_ops, priv);
> -	if (!br)
> -		return -ENOMEM;
> +	br = fpga_bridge_register(dev, FREEZE_BRIDGE_NAME,
> +				  &altera_freeze_br_br_ops, priv);
> +	if (IS_ERR(br))
> +		return PTR_ERR(br);
>  
>  	platform_set_drvdata(pdev, br);
>  
> -	return fpga_bridge_register(br);
> +	return 0;
>  }
>  
>  static int altera_freeze_br_remove(struct platform_device *pdev)
> diff --git a/drivers/fpga/altera-hps2fpga.c b/drivers/fpga/altera-hps2fpga.c
> index 77b95f251821..aa758426c22b 100644
> --- a/drivers/fpga/altera-hps2fpga.c
> +++ b/drivers/fpga/altera-hps2fpga.c
> @@ -180,19 +180,15 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev)
>  		}
>  	}
>  
> -	br = devm_fpga_bridge_create(dev, priv->name,
> -				     &altera_hps2fpga_br_ops, priv);
> -	if (!br) {
> -		ret = -ENOMEM;
> +	br = fpga_bridge_register(dev, priv->name,
> +				  &altera_hps2fpga_br_ops, priv);
> +	if (IS_ERR(br)) {
> +		ret = PTR_ERR(br);
>  		goto err;
>  	}
>  
>  	platform_set_drvdata(pdev, br);
>  
> -	ret = fpga_bridge_register(br);
> -	if (ret)
> -		goto err;
> -
>  	return 0;
>  
>  err:
> diff --git a/drivers/fpga/dfl-fme-br.c b/drivers/fpga/dfl-fme-br.c
> index 3ff9f3a687ce..808d1f4d76df 100644
> --- a/drivers/fpga/dfl-fme-br.c
> +++ b/drivers/fpga/dfl-fme-br.c
> @@ -68,14 +68,14 @@ static int fme_br_probe(struct platform_device *pdev)
>  
>  	priv->pdata = dev_get_platdata(dev);
>  
> -	br = devm_fpga_bridge_create(dev, "DFL FPGA FME Bridge",
> -				     &fme_bridge_ops, priv);
> -	if (!br)
> -		return -ENOMEM;
> +	br = fpga_bridge_register(dev, "DFL FPGA FME Bridge",
> +				  &fme_bridge_ops, priv);
> +	if (IS_ERR(br))
> +		return PTR_ERR(br);
>  
>  	platform_set_drvdata(pdev, br);
>  
> -	return fpga_bridge_register(br);
> +	return 0;
>  }
>  
>  static int fme_br_remove(struct platform_device *pdev)
> diff --git a/drivers/fpga/fpga-bridge.c b/drivers/fpga/fpga-bridge.c
> index 798f55670646..15be378b64ab 100644
> --- a/drivers/fpga/fpga-bridge.c
> +++ b/drivers/fpga/fpga-bridge.c
> @@ -312,36 +312,35 @@ static struct attribute *fpga_bridge_attrs[] = {
>  ATTRIBUTE_GROUPS(fpga_bridge);
>  
>  /**
> - * fpga_bridge_create - create and initialize a struct fpga_bridge
> + * fpga_bridge_register - create and register a FPGA Bridge device
>   * @parent:	FPGA bridge device from pdev
>   * @name:	FPGA bridge name
>   * @br_ops:	pointer to structure of fpga bridge ops
>   * @priv:	FPGA bridge private data
>   *
> - * The caller of this function is responsible for freeing the bridge with
> - * fpga_bridge_free().  Using devm_fpga_bridge_create() instead is recommended.
> - *
> - * Return: struct fpga_bridge or NULL
> + * Return: struct fpga_bridge pointer or ERR_PTR()
>   */
> -struct fpga_bridge *fpga_bridge_create(struct device *parent, const char *name,
> -				       const struct fpga_bridge_ops *br_ops,
> -				       void *priv)
> +struct fpga_bridge *fpga_bridge_register(struct device *parent, const char *name,
> +					 const struct fpga_bridge_ops *br_ops,
> +					 void *priv)

Is it better we use the same style as fpga_mgr_register?

Thanks,
Yilun

>  {
>  	struct fpga_bridge *bridge;
>  	int id, ret;
>  
>  	if (!name || !strlen(name)) {
>  		dev_err(parent, "Attempt to register with no name!\n");
> -		return NULL;
> +		return ERR_PTR(-EINVAL);
>  	}
>  
>  	bridge = kzalloc(sizeof(*bridge), GFP_KERNEL);
>  	if (!bridge)
> -		return NULL;
> +		return ERR_PTR(-ENOMEM);
>  
>  	id = ida_simple_get(&fpga_bridge_ida, 0, 0, GFP_KERNEL);
> -	if (id < 0)
> +	if (id < 0) {
> +		ret = id;
>  		goto error_kfree;
> +	}
>  
>  	mutex_init(&bridge->mutex);
>  	INIT_LIST_HEAD(&bridge->node);
> @@ -350,17 +349,23 @@ struct fpga_bridge *fpga_bridge_create(struct device *parent, const char *name,
>  	bridge->br_ops = br_ops;
>  	bridge->priv = priv;
>  
> -	device_initialize(&bridge->dev);
>  	bridge->dev.groups = br_ops->groups;
>  	bridge->dev.class = fpga_bridge_class;
>  	bridge->dev.parent = parent;
>  	bridge->dev.of_node = parent->of_node;
>  	bridge->dev.id = id;
> +	of_platform_populate(bridge->dev.of_node, NULL, NULL, &bridge->dev);
>  
>  	ret = dev_set_name(&bridge->dev, "br%d", id);
>  	if (ret)
>  		goto error_device;
>  
> +	ret = device_register(&bridge->dev);
> +	if (ret) {
> +		put_device(&bridge->dev);
> +		return ERR_PTR(ret);
> +	}
> +
>  	return bridge;
>  
>  error_device:
> @@ -368,88 +373,7 @@ struct fpga_bridge *fpga_bridge_create(struct device *parent, const char *name,
>  error_kfree:
>  	kfree(bridge);
>  
> -	return NULL;
> -}
> -EXPORT_SYMBOL_GPL(fpga_bridge_create);
> -
> -/**
> - * fpga_bridge_free - free an fpga bridge created by fpga_bridge_create()
> - * @bridge:	FPGA bridge struct
> - */
> -void fpga_bridge_free(struct fpga_bridge *bridge)
> -{
> -	ida_simple_remove(&fpga_bridge_ida, bridge->dev.id);
> -	kfree(bridge);
> -}
> -EXPORT_SYMBOL_GPL(fpga_bridge_free);
> -
> -static void devm_fpga_bridge_release(struct device *dev, void *res)
> -{
> -	struct fpga_bridge *bridge = *(struct fpga_bridge **)res;
> -
> -	fpga_bridge_free(bridge);
> -}
> -
> -/**
> - * devm_fpga_bridge_create - create and init a managed struct fpga_bridge
> - * @parent:	FPGA bridge device from pdev
> - * @name:	FPGA bridge name
> - * @br_ops:	pointer to structure of fpga bridge ops
> - * @priv:	FPGA bridge private data
> - *
> - * This function is intended for use in an FPGA bridge driver's probe function.
> - * After the bridge driver creates the struct with devm_fpga_bridge_create(), it
> - * should register the bridge with fpga_bridge_register().  The bridge driver's
> - * remove function should call fpga_bridge_unregister().  The bridge 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_bridge_register(), the struct will still get cleaned up.
> - *
> - *  Return: struct fpga_bridge or NULL
> - */
> -struct fpga_bridge
> -*devm_fpga_bridge_create(struct device *parent, const char *name,
> -			 const struct fpga_bridge_ops *br_ops, void *priv)
> -{
> -	struct fpga_bridge **ptr, *bridge;
> -
> -	ptr = devres_alloc(devm_fpga_bridge_release, sizeof(*ptr), GFP_KERNEL);
> -	if (!ptr)
> -		return NULL;
> -
> -	bridge = fpga_bridge_create(parent, name, br_ops, priv);
> -	if (!bridge) {
> -		devres_free(ptr);
> -	} else {
> -		*ptr = bridge;
> -		devres_add(parent, ptr);
> -	}
> -
> -	return bridge;
> -}
> -EXPORT_SYMBOL_GPL(devm_fpga_bridge_create);
> -
> -/**
> - * fpga_bridge_register - register an FPGA bridge
> - *
> - * @bridge: FPGA bridge struct
> - *
> - * Return: 0 for success, error code otherwise.
> - */
> -int fpga_bridge_register(struct fpga_bridge *bridge)
> -{
> -	struct device *dev = &bridge->dev;
> -	int ret;
> -
> -	ret = device_add(dev);
> -	if (ret)
> -		return ret;
> -
> -	of_platform_populate(dev->of_node, NULL, NULL, dev);
> -
> -	dev_info(dev->parent, "fpga bridge [%s] registered\n", bridge->name);
> -
> -	return 0;
> +	return ERR_PTR(ret);
>  }
>  EXPORT_SYMBOL_GPL(fpga_bridge_register);
>  
> @@ -475,6 +399,10 @@ EXPORT_SYMBOL_GPL(fpga_bridge_unregister);
>  
>  static void fpga_bridge_dev_release(struct device *dev)
>  {
> +	struct fpga_bridge *bridge = to_fpga_bridge(dev);
> +
> +	ida_simple_remove(&fpga_bridge_ida, bridge->dev.id);
> +	kfree(bridge);
>  }
>  
>  static int __init fpga_bridge_dev_init(void)
> diff --git a/drivers/fpga/xilinx-pr-decoupler.c b/drivers/fpga/xilinx-pr-decoupler.c
> index ea2bde6e5bc4..c004e52b9464 100644
> --- a/drivers/fpga/xilinx-pr-decoupler.c
> +++ b/drivers/fpga/xilinx-pr-decoupler.c
> @@ -138,22 +138,17 @@ static int xlnx_pr_decoupler_probe(struct platform_device *pdev)
>  
>  	clk_disable(priv->clk);
>  
> -	br = devm_fpga_bridge_create(&pdev->dev, priv->ipconfig->name,
> -				     &xlnx_pr_decoupler_br_ops, priv);
> -	if (!br) {
> -		err = -ENOMEM;
> -		goto err_clk;
> -	}
> -
> -	platform_set_drvdata(pdev, br);
> -
> -	err = fpga_bridge_register(br);
> -	if (err) {
> +	br = fpga_bridge_register(&pdev->dev, priv->ipconfig->name,
> +				  &xlnx_pr_decoupler_br_ops, priv);
> +	if (IS_ERR(br)) {
> +		err = PTR_ERR(br);
>  		dev_err(&pdev->dev, "unable to register %s",
>  			priv->ipconfig->name);
>  		goto err_clk;
>  	}
>  
> +	platform_set_drvdata(pdev, br);
> +
>  	return 0;
>  
>  err_clk:
> diff --git a/include/linux/fpga/fpga-bridge.h b/include/linux/fpga/fpga-bridge.h
> index 6c3c28806ff1..2f0eb62c49f5 100644
> --- a/include/linux/fpga/fpga-bridge.h
> +++ b/include/linux/fpga/fpga-bridge.h
> @@ -62,15 +62,9 @@ int of_fpga_bridge_get_to_list(struct device_node *np,
>  			       struct fpga_image_info *info,
>  			       struct list_head *bridge_list);
>  
> -struct fpga_bridge *fpga_bridge_create(struct device *dev, const char *name,
> -				       const struct fpga_bridge_ops *br_ops,
> -				       void *priv);
> -void fpga_bridge_free(struct fpga_bridge *br);
> -int fpga_bridge_register(struct fpga_bridge *br);
> +struct fpga_bridge *fpga_bridge_register(struct device *dev, const char *name,
> +					 const struct fpga_bridge_ops *br_ops,
> +					 void *priv);
>  void fpga_bridge_unregister(struct fpga_bridge *br);
>  
> -struct fpga_bridge
> -*devm_fpga_bridge_create(struct device *dev, const char *name,
> -			 const struct fpga_bridge_ops *br_ops, void *priv);
> -
>  #endif /* _LINUX_FPGA_BRIDGE_H */
> -- 
> 2.25.1

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v5 3/3] fpga: region: Use standard dev_release for class driver
  2021-06-16 22:57 ` [PATCH v5 3/3] fpga: region: " Russ Weight
@ 2021-06-18 15:54   ` Xu Yilun
  2021-06-18 16:06     ` Russ Weight
  0 siblings, 1 reply; 19+ messages in thread
From: Xu Yilun @ 2021-06-18 15:54 UTC (permalink / raw)
  To: Russ Weight
  Cc: mdf, linux-fpga, trix, lgoncalv, hao.wu, matthew.gerlach, richard.gong

On Wed, Jun 16, 2021 at 03:57:40PM -0700, Russ Weight wrote:
> The FPGA region class driver data structure is being treated as a
> managed resource instead of using standard dev_release call-back
> to release the class data structure. This change removes the
> managed resource code and combines the create() and register()
> functions into a single register() function.
> 
> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
> ---
> v5:
>   - Rebased on top of recently accepted patches.
>   - Created the fpga_region_ops data structure which is optionally passed
>     to fpga_region_register(). compat_id, the get_bridges() pointer, and
>     the priv pointer are included in the fpga_region_ops structure.
> v4:
>   - Added the compat_id parameter to fpga_region_register() to ensure
>     that the compat_id is set before the device_register() call.
>   - Modified the dfl_fpga_feature_devs_enumerate() function to restore
>     the fpga_region_register() call to the correct location.
> v3:
>   - Cleaned up comment header for fpga_region_register()
>   - Fix fpga_region_register() error return on ida_simple_get() failure
> v2:
>   - No changes
> ---
>  drivers/fpga/dfl-fme-pr.c        |   2 +-
>  drivers/fpga/dfl-fme-region.c    |  32 ++++++---
>  drivers/fpga/dfl.c               |  12 ++--
>  drivers/fpga/fpga-bridge.c       |   2 +-
>  drivers/fpga/fpga-region.c       | 119 ++++++++-----------------------
>  drivers/fpga/of-fpga-region.c    |  14 ++--
>  include/linux/fpga/fpga-region.h |  38 ++++++----
>  7 files changed, 86 insertions(+), 133 deletions(-)
> 
> diff --git a/drivers/fpga/dfl-fme-pr.c b/drivers/fpga/dfl-fme-pr.c
> index d61ce9a18879..4805d8c533d4 100644
> --- a/drivers/fpga/dfl-fme-pr.c
> +++ b/drivers/fpga/dfl-fme-pr.c
> @@ -151,7 +151,7 @@ static int fme_pr(struct platform_device *pdev, unsigned long arg)
>  	 * reenabling the bridge to clear things out between acceleration runs.
>  	 * so no need to hold the bridges after partial reconfiguration.
>  	 */
> -	if (region->get_bridges)
> +	if (region->rops && region->rops->get_bridges)
>  		fpga_bridges_put(&region->bridge_list);
>  
>  	put_device(&region->dev);
> diff --git a/drivers/fpga/dfl-fme-region.c b/drivers/fpga/dfl-fme-region.c
> index a64c13e198a0..16d8b3f6f07a 100644
> --- a/drivers/fpga/dfl-fme-region.c
> +++ b/drivers/fpga/dfl-fme-region.c
> @@ -21,12 +21,29 @@
>  
>  static int fme_region_get_bridges(struct fpga_region *region)
>  {
> -	struct dfl_fme_region_pdata *pdata = region->priv;
> +	struct dfl_fme_region_pdata *pdata = region->rops->priv;
>  	struct device *dev = &pdata->br->dev;
>  
>  	return fpga_bridge_get_to_list(dev, region->info, &region->bridge_list);
>  }
>  
> +static struct fpga_region_ops *
> +fme_region_get_ops(struct device *dev, struct fpga_manager *mgr,
> +		   struct dfl_fme_region_pdata *pdata)
> +{
> +	struct fpga_region_ops *ops;
> +
> +	ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL);
> +	if (!ops)
> +		return NULL;
> +
> +	ops->get_bridges = fme_region_get_bridges;
> +	ops->compat_id = mgr->mops->compat_id;
> +	ops->priv = pdata;
> +
> +	return ops;
> +}
> +
>  static int fme_region_probe(struct platform_device *pdev)
>  {
>  	struct dfl_fme_region_pdata *pdata = dev_get_platdata(&pdev->dev);
> @@ -39,20 +56,15 @@ static int fme_region_probe(struct platform_device *pdev)
>  	if (IS_ERR(mgr))
>  		return -EPROBE_DEFER;
>  
> -	region = devm_fpga_region_create(dev, mgr, fme_region_get_bridges);
> -	if (!region) {
> -		ret = -ENOMEM;
> +	region = fpga_region_register(dev, mgr,
> +				      fme_region_get_ops(dev, mgr, pdata));
> +	if (IS_ERR(region)) {
> +		ret = PTR_ERR(region);
>  		goto eprobe_mgr_put;
>  	}
>  
> -	region->priv = pdata;
> -	region->compat_id = mgr->mops->compat_id;
>  	platform_set_drvdata(pdev, region);
>  
> -	ret = fpga_region_register(region);
> -	if (ret)
> -		goto eprobe_mgr_put;
> -
>  	dev_dbg(dev, "DFL FME FPGA Region probed\n");
>  
>  	return 0;
> diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c
> index 511b20ff35a3..3905995ef6d5 100644
> --- a/drivers/fpga/dfl.c
> +++ b/drivers/fpga/dfl.c
> @@ -1400,19 +1400,15 @@ dfl_fpga_feature_devs_enumerate(struct dfl_fpga_enum_info *info)
>  	if (!cdev)
>  		return ERR_PTR(-ENOMEM);
>  
> -	cdev->region = devm_fpga_region_create(info->dev, NULL, NULL);
> -	if (!cdev->region) {
> -		ret = -ENOMEM;
> -		goto free_cdev_exit;
> -	}
> -
>  	cdev->parent = info->dev;
>  	mutex_init(&cdev->lock);
>  	INIT_LIST_HEAD(&cdev->port_dev_list);
>  
> -	ret = fpga_region_register(cdev->region);
> -	if (ret)
> +	cdev->region = fpga_region_register(info->dev, NULL, NULL);
> +	if (IS_ERR(cdev->region)) {
> +		ret = PTR_ERR(cdev->region);
>  		goto free_cdev_exit;
> +	}
>  
>  	/* create and init build info for enumeration */
>  	binfo = devm_kzalloc(info->dev, sizeof(*binfo), GFP_KERNEL);
> diff --git a/drivers/fpga/fpga-bridge.c b/drivers/fpga/fpga-bridge.c
> index 15be378b64ab..f7e8500561f4 100644
> --- a/drivers/fpga/fpga-bridge.c
> +++ b/drivers/fpga/fpga-bridge.c
> @@ -312,7 +312,7 @@ static struct attribute *fpga_bridge_attrs[] = {
>  ATTRIBUTE_GROUPS(fpga_bridge);
>  
>  /**
> - * fpga_bridge_register - create and register a FPGA Bridge device
> + * fpga_bridge_register - create and register an FPGA Bridge device
>   * @parent:	FPGA bridge device from pdev
>   * @name:	FPGA bridge name
>   * @br_ops:	pointer to structure of fpga bridge ops
> diff --git a/drivers/fpga/fpga-region.c b/drivers/fpga/fpga-region.c
> index a4838715221f..7199ad93814f 100644
> --- a/drivers/fpga/fpga-region.c
> +++ b/drivers/fpga/fpga-region.c
> @@ -115,8 +115,8 @@ int fpga_region_program_fpga(struct fpga_region *region)
>  	 * In some cases, we already have a list of bridges in the
>  	 * fpga region struct.  Or we don't have any bridges.
>  	 */
> -	if (region->get_bridges) {
> -		ret = region->get_bridges(region);
> +	if (region->rops && region->rops->get_bridges) {
> +		ret = region->rops->get_bridges(region);
>  		if (ret) {
>  			dev_err(dev, "failed to get fpga region bridges\n");
>  			goto err_unlock_mgr;
> @@ -147,7 +147,7 @@ int fpga_region_program_fpga(struct fpga_region *region)
>  	return 0;
>  
>  err_put_br:
> -	if (region->get_bridges)
> +	if (region->rops && region->rops->get_bridges)
>  		fpga_bridges_put(&region->bridge_list);
>  err_unlock_mgr:
>  	fpga_mgr_unlock(region->mgr);
> @@ -163,12 +163,12 @@ static ssize_t compat_id_show(struct device *dev,
>  {
>  	struct fpga_region *region = to_fpga_region(dev);
>  
> -	if (!region->compat_id)
> +	if (!region->rops || !region->rops->compat_id)
>  		return -ENOENT;
>  
>  	return sprintf(buf, "%016llx%016llx\n",
> -		       (unsigned long long)region->compat_id->id_h,
> -		       (unsigned long long)region->compat_id->id_l);
> +		       (unsigned long long)region->rops->compat_id->id_h,
> +		       (unsigned long long)region->rops->compat_id->id_l);
>  }
>  
>  static DEVICE_ATTR_RO(compat_id);
> @@ -180,39 +180,35 @@ static struct attribute *fpga_region_attrs[] = {
>  ATTRIBUTE_GROUPS(fpga_region);
>  
>  /**
> - * fpga_region_create - alloc and init a struct fpga_region
> + * fpga_region_register - create and register an FPGA Region device
>   * @parent: device parent
>   * @mgr: manager that programs this region
> - * @get_bridges: optional function to get bridges to a list
> + * @rops: optional: FPGA Region ops for low level FPGA Region drivers
>   *
> - * The caller of this function is responsible for freeing the resulting region
> - * struct with fpga_region_free().  Using devm_fpga_region_create() instead is
> - * recommended.
> - *
> - * Return: struct fpga_region or NULL
> + * Return: struct fpga_region or ERR_PTR()
>   */
> -struct fpga_region
> -*fpga_region_create(struct device *parent,
> -		    struct fpga_manager *mgr,
> -		    int (*get_bridges)(struct fpga_region *))
> +struct fpga_region *
> +fpga_region_register(struct device *parent, struct fpga_manager *mgr,
> +		     struct fpga_region_ops *rops)

Is it better we use the same style as fpga_mgr_register?

Thanks,
Yilun

>  {
>  	struct fpga_region *region;
>  	int id, ret = 0;
>  
>  	region = kzalloc(sizeof(*region), GFP_KERNEL);
>  	if (!region)
> -		return NULL;
> +		return ERR_PTR(-ENOMEM);
>  
>  	id = ida_simple_get(&fpga_region_ida, 0, 0, GFP_KERNEL);
> -	if (id < 0)
> +	if (id < 0) {
> +		ret = id;
>  		goto err_free;
> +	}
>  
>  	region->mgr = mgr;
> -	region->get_bridges = get_bridges;
> +	region->rops = rops;
>  	mutex_init(&region->mutex);
>  	INIT_LIST_HEAD(&region->bridge_list);
>  
> -	device_initialize(&region->dev);
>  	region->dev.class = fpga_region_class;
>  	region->dev.parent = parent;
>  	region->dev.of_node = parent->of_node;
> @@ -222,6 +218,12 @@ struct fpga_region
>  	if (ret)
>  		goto err_remove;
>  
> +	ret = device_register(&region->dev);
> +	if (ret) {
> +		put_device(&region->dev);
> +		return ERR_PTR(ret);
> +	}
> +
>  	return region;
>  
>  err_remove:
> @@ -229,76 +231,7 @@ struct fpga_region
>  err_free:
>  	kfree(region);
>  
> -	return NULL;
> -}
> -EXPORT_SYMBOL_GPL(fpga_region_create);
> -
> -/**
> - * fpga_region_free - free an FPGA region created by fpga_region_create()
> - * @region: FPGA region
> - */
> -void fpga_region_free(struct fpga_region *region)
> -{
> -	ida_simple_remove(&fpga_region_ida, region->dev.id);
> -	kfree(region);
> -}
> -EXPORT_SYMBOL_GPL(fpga_region_free);
> -
> -static void devm_fpga_region_release(struct device *dev, void *res)
> -{
> -	struct fpga_region *region = *(struct fpga_region **)res;
> -
> -	fpga_region_free(region);
> -}
> -
> -/**
> - * devm_fpga_region_create - create and initialize a managed FPGA region struct
> - * @parent: device parent
> - * @mgr: manager that programs this region
> - * @get_bridges: optional function to get bridges to a list
> - *
> - * This function is intended for use in an FPGA region driver's probe function.
> - * After the region driver creates the region struct with
> - * devm_fpga_region_create(), it should register it with fpga_region_register().
> - * The region driver's remove function should call fpga_region_unregister().
> - * The region 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_region_register(), the struct will still get cleaned up.
> - *
> - * Return: struct fpga_region or NULL
> - */
> -struct fpga_region
> -*devm_fpga_region_create(struct device *parent,
> -			 struct fpga_manager *mgr,
> -			 int (*get_bridges)(struct fpga_region *))
> -{
> -	struct fpga_region **ptr, *region;
> -
> -	ptr = devres_alloc(devm_fpga_region_release, sizeof(*ptr), GFP_KERNEL);
> -	if (!ptr)
> -		return NULL;
> -
> -	region = fpga_region_create(parent, mgr, get_bridges);
> -	if (!region) {
> -		devres_free(ptr);
> -	} else {
> -		*ptr = region;
> -		devres_add(parent, ptr);
> -	}
> -
> -	return region;
> -}
> -EXPORT_SYMBOL_GPL(devm_fpga_region_create);
> -
> -/**
> - * fpga_region_register - register an FPGA region
> - * @region: FPGA region
> - *
> - * Return: 0 or -errno
> - */
> -int fpga_region_register(struct fpga_region *region)
> -{
> -	return device_add(&region->dev);
> +	return ERR_PTR(ret);
>  }
>  EXPORT_SYMBOL_GPL(fpga_region_register);
>  
> @@ -316,6 +249,10 @@ EXPORT_SYMBOL_GPL(fpga_region_unregister);
>  
>  static void fpga_region_dev_release(struct device *dev)
>  {
> +	struct fpga_region *region = to_fpga_region(dev);
> +
> +	ida_simple_remove(&fpga_region_ida, region->dev.id);
> +	kfree(region);
>  }
>  
>  /**
> diff --git a/drivers/fpga/of-fpga-region.c b/drivers/fpga/of-fpga-region.c
> index e3c25576b6b9..55b7c4ec8f6d 100644
> --- a/drivers/fpga/of-fpga-region.c
> +++ b/drivers/fpga/of-fpga-region.c
> @@ -392,6 +392,10 @@ static struct notifier_block fpga_region_of_nb = {
>  	.notifier_call = of_fpga_region_notify,
>  };
>  
> +static struct fpga_region_ops of_fpga_region_ops = {
> +	.get_bridges = of_fpga_region_get_bridges;
> +};
> +
>  static int of_fpga_region_probe(struct platform_device *pdev)
>  {
>  	struct device *dev = &pdev->dev;
> @@ -405,16 +409,12 @@ static int of_fpga_region_probe(struct platform_device *pdev)
>  	if (IS_ERR(mgr))
>  		return -EPROBE_DEFER;
>  
> -	region = devm_fpga_region_create(dev, mgr, of_fpga_region_get_bridges);
> -	if (!region) {
> -		ret = -ENOMEM;
> +	region = fpga_region_register(dev, mgr, &of_fpga_region_ops);
> +	if (IS_ERR(region)) {
> +		ret = PTR_ERR(region);
>  		goto eprobe_mgr_put;
>  	}
>  
> -	ret = fpga_region_register(region);
> -	if (ret)
> -		goto eprobe_mgr_put;
> -
>  	of_platform_populate(np, fpga_region_of_match, NULL, &region->dev);
>  	platform_set_drvdata(pdev, region);
>  
> diff --git a/include/linux/fpga/fpga-region.h b/include/linux/fpga/fpga-region.h
> index 27cb706275db..02626dfd41ed 100644
> --- a/include/linux/fpga/fpga-region.h
> +++ b/include/linux/fpga/fpga-region.h
> @@ -7,6 +7,24 @@
>  #include <linux/fpga/fpga-mgr.h>
>  #include <linux/fpga/fpga-bridge.h>
>  
> +struct fpga_region;
> +
> +/**
> + * struct fpga_region_ops - ops for low level fpga region drivers
> + * @get_bridges: optional function to get bridges to a list
> + * @compat_id: optional: FPGA manager id for compatibility check.
> + * @priv: optional: private data
> + *
> + * fpga_region_ops are the low level functions implemented by a specific
> + * fpga region driver.  The optional ones are tested for NULL before being
> + * referenced, so leaving them out is fine.
> + */
> +struct fpga_region_ops {
> +	int (*get_bridges)(struct fpga_region *region);
> +	struct fpga_compat_id *compat_id;
> +	void *priv;
> +};
> +
>  /**
>   * struct fpga_region - FPGA Region structure
>   * @dev: FPGA Region device
> @@ -14,9 +32,7 @@
>   * @bridge_list: list of FPGA bridges specified in region
>   * @mgr: FPGA manager
>   * @info: FPGA image info
> - * @compat_id: FPGA region id for compatibility check.
> - * @priv: private data
> - * @get_bridges: optional function to get bridges to a list
> + * @rops: optional: FPGA Region ops for low level FPGA region drivers
>   */
>  struct fpga_region {
>  	struct device dev;
> @@ -24,9 +40,7 @@ struct fpga_region {
>  	struct list_head bridge_list;
>  	struct fpga_manager *mgr;
>  	struct fpga_image_info *info;
> -	struct fpga_compat_id *compat_id;
> -	void *priv;
> -	int (*get_bridges)(struct fpga_region *region);
> +	struct fpga_region_ops *rops;
>  };
>  
>  #define to_fpga_region(d) container_of(d, struct fpga_region, dev)
> @@ -37,15 +51,9 @@ struct fpga_region *fpga_region_class_find(
>  
>  int fpga_region_program_fpga(struct fpga_region *region);
>  
> -struct fpga_region
> -*fpga_region_create(struct device *dev, struct fpga_manager *mgr,
> -		    int (*get_bridges)(struct fpga_region *));
> -void fpga_region_free(struct fpga_region *region);
> -int fpga_region_register(struct fpga_region *region);
> +struct fpga_region *
> +fpga_region_register(struct device *dev, struct fpga_manager *mgr,
> +		     struct fpga_region_ops *rops);
>  void fpga_region_unregister(struct fpga_region *region);
>  
> -struct fpga_region
> -*devm_fpga_region_create(struct device *dev, struct fpga_manager *mgr,
> -			int (*get_bridges)(struct fpga_region *));
> -
>  #endif /* _FPGA_REGION_H */
> -- 
> 2.25.1

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v5 1/3] fpga: mgr: Use standard dev_release for class driver
  2021-06-18 15:45   ` Xu Yilun
@ 2021-06-18 16:03     ` Russ Weight
  2021-06-18 17:58       ` Russ Weight
  0 siblings, 1 reply; 19+ messages in thread
From: Russ Weight @ 2021-06-18 16:03 UTC (permalink / raw)
  To: Xu Yilun
  Cc: mdf, linux-fpga, trix, lgoncalv, hao.wu, matthew.gerlach, richard.gong



On 6/18/21 8:45 AM, Xu Yilun wrote:
> On Wed, Jun 16, 2021 at 03:57:38PM -0700, Russ Weight wrote:
>> The FPGA manager class driver data structure is being treated as a
>> managed resource instead of using standard dev_release call-back
>> to release the class data structure. This change removes the
>> managed resource code for the freeing of the class data structure
>> and combines the create() and register() functions into a single
>> register() function.
>>
>> The devm_fpga_mgr_register() function is retained.
>>
>> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
>> ---
>> v5:
>>   - Rebased on top of recently accepted patches.
>>   - Removed compat_id from the fpga_mgr_register() parameter list
>>     and added it to the fpga_manager_ops structure. This also required
> My reason for this change is, we could avoid further change of the
> fpga_mgr_register() API if we add new input parameters later on.

With this patchset, changes are only required for the callers
that use the new parameters.

>
>>     dynamically allocating the dfl-fme-ops structure in order to add
>>     the appropriate compat_id.
> But enforcing the dynamical allocation of the parameters is not prefered
> to me. How about a dedicated structure that wraps all the needed
> parameters:
>
> struct fpga_mgr_info {
> 	const char *name;
> 	const struct fpga_manager_ops *mops;
> 	const struct fpga_compat_id *compat_id;
> 	const void *priv;
> };
>
> Then We can simply define a local variable of this struct for
> fpga_mgr_register().
>
> more details inline.

I agree the at the dynamic allocation is not preferred, but it is only
required if compat_id is used. In all other cases, the static structure
can continue to be used. In otherwords, only one caller is affected.
>
>
>> v4:
>>   - Added the compat_id parameter to fpga_mgr_register() and
>>     devm_fpga_mgr_register() to ensure that the compat_id is set before
>>     the device_register() call.
>> v3:
>>   - Cleaned up comment header for fpga_mgr_register()
>>   - Fix error return on ida_simple_get() failure
>> v2:
>>   - Restored devm_fpga_mgr_register() functionality, adapted for the combined
>>     create/register functionality.
>>   - All previous callers of devm_fpga_mgr_register() will continue to call
>>     devm_fpga_mgr_register().
>>   - replaced unnecessary ternary operators in return statements with standard
>>     if conditions.
>> ---
>>  drivers/fpga/altera-cvp.c        |  12 +--
>>  drivers/fpga/altera-pr-ip-core.c |   8 +-
>>  drivers/fpga/altera-ps-spi.c     |  10 +-
>>  drivers/fpga/dfl-fme-mgr.c       |  52 ++++++----
>>  drivers/fpga/dfl-fme-region.c    |   2 +-
>>  drivers/fpga/fpga-mgr.c          | 163 ++++++++-----------------------
>>  drivers/fpga/ice40-spi.c         |  10 +-
>>  drivers/fpga/machxo2-spi.c       |  10 +-
>>  drivers/fpga/socfpga-a10.c       |  16 ++-
>>  drivers/fpga/socfpga.c           |  10 +-
>>  drivers/fpga/stratix10-soc.c     |  16 +--
>>  drivers/fpga/ts73xx-fpga.c       |  10 +-
>>  drivers/fpga/xilinx-spi.c        |  12 +--
>>  drivers/fpga/zynq-fpga.c         |  16 ++-
>>  drivers/fpga/zynqmp-fpga.c       |  10 +-
>>  include/linux/fpga/fpga-mgr.h    |  43 ++++----
>>  16 files changed, 153 insertions(+), 247 deletions(-)
>>
>> diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
>> index ccf4546eff29..4ffb9da537d8 100644
>> --- a/drivers/fpga/altera-cvp.c
>> +++ b/drivers/fpga/altera-cvp.c
>> @@ -652,19 +652,15 @@ static int altera_cvp_probe(struct pci_dev *pdev,
>>  	snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s @%s",
>>  		 ALTERA_CVP_MGR_NAME, pci_name(pdev));
>>  
>> -	mgr = devm_fpga_mgr_create(&pdev->dev, conf->mgr_name,
>> -				   &altera_cvp_ops, conf);
>> -	if (!mgr) {
>> -		ret = -ENOMEM;
>> +	mgr = fpga_mgr_register(&pdev->dev, conf->mgr_name,
>> +				&altera_cvp_ops, conf);
>> +	if (IS_ERR(mgr)) {
>> +		ret = PTR_ERR(mgr);
>>  		goto err_unmap;
>>  	}
>>  
>>  	pci_set_drvdata(pdev, mgr);
>>  
>> -	ret = fpga_mgr_register(mgr);
>> -	if (ret)
>> -		goto err_unmap;
>> -
>>  	return 0;
>>  
>>  err_unmap:
>> diff --git a/drivers/fpga/altera-pr-ip-core.c b/drivers/fpga/altera-pr-ip-core.c
>> index dfdf21ed34c4..17babf974852 100644
>> --- a/drivers/fpga/altera-pr-ip-core.c
>> +++ b/drivers/fpga/altera-pr-ip-core.c
>> @@ -191,11 +191,11 @@ int alt_pr_register(struct device *dev, void __iomem *reg_base)
>>  		(val & ALT_PR_CSR_STATUS_MSK) >> ALT_PR_CSR_STATUS_SFT,
>>  		(int)(val & ALT_PR_CSR_PR_START));
>>  
>> -	mgr = devm_fpga_mgr_create(dev, dev_name(dev), &alt_pr_ops, priv);
>> -	if (!mgr)
>> -		return -ENOMEM;
>> +	mgr = devm_fpga_mgr_register(dev, dev_name(dev), &alt_pr_ops, priv);
>> +	if (IS_ERR(mgr))
>> +		return PTR_ERR(mgr);
>>  
>> -	return devm_fpga_mgr_register(dev, mgr);
>> +	return 0;
>>  }
>>  EXPORT_SYMBOL_GPL(alt_pr_register);
>>  
>> diff --git a/drivers/fpga/altera-ps-spi.c b/drivers/fpga/altera-ps-spi.c
>> index 23bfd4d1ad0f..d3f77b0312b2 100644
>> --- a/drivers/fpga/altera-ps-spi.c
>> +++ b/drivers/fpga/altera-ps-spi.c
>> @@ -302,12 +302,12 @@ static int altera_ps_probe(struct spi_device *spi)
>>  	snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s %s",
>>  		 dev_driver_string(&spi->dev), dev_name(&spi->dev));
>>  
>> -	mgr = devm_fpga_mgr_create(&spi->dev, conf->mgr_name,
>> -				   &altera_ps_ops, conf);
>> -	if (!mgr)
>> -		return -ENOMEM;
>> +	mgr = devm_fpga_mgr_register(&spi->dev, conf->mgr_name,
>> +				     &altera_ps_ops, conf);
>> +	if (IS_ERR(mgr))
>> +		return PTR_ERR(mgr);
>>  
>> -	return devm_fpga_mgr_register(&spi->dev, mgr);
>> +	return 0;
>>  }
>>  
>>  static const struct spi_device_id altera_ps_spi_ids[] = {
>> diff --git a/drivers/fpga/dfl-fme-mgr.c b/drivers/fpga/dfl-fme-mgr.c
>> index d5861d13b306..1b93bc292dbe 100644
>> --- a/drivers/fpga/dfl-fme-mgr.c
>> +++ b/drivers/fpga/dfl-fme-mgr.c
>> @@ -264,14 +264,6 @@ static u64 fme_mgr_status(struct fpga_manager *mgr)
>>  	return pr_error_to_mgr_status(priv->pr_error);
>>  }
>>  
>> -static const struct fpga_manager_ops fme_mgr_ops = {
>> -	.write_init = fme_mgr_write_init,
>> -	.write = fme_mgr_write,
>> -	.write_complete = fme_mgr_write_complete,
>> -	.state = fme_mgr_state,
>> -	.status = fme_mgr_status,
>> -};
>> -
>>  static void fme_mgr_get_compat_id(void __iomem *fme_pr,
>>  				  struct fpga_compat_id *id)
>>  {
>> @@ -279,10 +271,34 @@ static void fme_mgr_get_compat_id(void __iomem *fme_pr,
>>  	id->id_h = readq(fme_pr + FME_PR_INTFC_ID_H);
>>  }
>>  
>> +static struct fpga_manager_ops *fme_mgr_get_ops(struct device *dev,
>> +						struct fme_mgr_priv *priv)
>> +{
>> +	struct fpga_manager_ops *ops;
>> +
>> +	ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL);
>> +	if (!ops)
>> +		return NULL;
>> +
>> +	ops->compat_id = devm_kzalloc(dev, sizeof(struct fpga_compat_id),
>> +				      GFP_KERNEL);
>> +	if (!ops->compat_id)
>> +		return NULL;
>> +
>> +	fme_mgr_get_compat_id(priv->ioaddr, ops->compat_id);
>> +	ops->write_init = fme_mgr_write_init;
>> +	ops->write = fme_mgr_write;
>> +	ops->write_complete = fme_mgr_write_complete;
>> +	ops->state = fme_mgr_state;
>> +	ops->status = fme_mgr_status;
>> +
>> +	return ops;
>> +}
>> +
>>  static int fme_mgr_probe(struct platform_device *pdev)
>>  {
>>  	struct dfl_fme_mgr_pdata *pdata = dev_get_platdata(&pdev->dev);
>> -	struct fpga_compat_id *compat_id;
>> +	struct fpga_manager_ops *fme_mgr_ops;
>>  	struct device *dev = &pdev->dev;
>>  	struct fme_mgr_priv *priv;
>>  	struct fpga_manager *mgr;
>> @@ -302,20 +318,16 @@ static int fme_mgr_probe(struct platform_device *pdev)
>>  			return PTR_ERR(priv->ioaddr);
>>  	}
>>  
>> -	compat_id = devm_kzalloc(dev, sizeof(*compat_id), GFP_KERNEL);
>> -	if (!compat_id)
>> -		return -ENOMEM;
>> -
>> -	fme_mgr_get_compat_id(priv->ioaddr, compat_id);
>> -
>> -	mgr = devm_fpga_mgr_create(dev, "DFL FME FPGA Manager",
>> -				   &fme_mgr_ops, priv);
>> -	if (!mgr)
>> +	fme_mgr_ops = fme_mgr_get_ops(dev, priv);
>> +	if (!fme_mgr_ops)
>>  		return -ENOMEM;
>>  
>> -	mgr->compat_id = compat_id;
>> +	mgr = devm_fpga_mgr_register(dev, "DFL FME FPGA Manager",
>> +				     fme_mgr_ops, priv);
>> +	if (IS_ERR(mgr))
>> +		return PTR_ERR(mgr);
> With structure fpga_mgr_info, we don't need the dynamical allocation
> anymore:
>
> 	struct fpga_mgr_info minfo = { 0 };
>
> 	minfo.name = "DFL FME FPGA Manager";
> 	minfo.mops = &fme_mgr_ops;
> 	minfo.compat_id = compat_id;
> 	minfo.priv = priv;
>
> 	mgr = devm_fpga_mgr_register(dev, &minfo);
>
> Drivers could also leaves minfo.compat_id zero if they don't care.
>
> I also expect more opinions.
>
>>  
>> -	return devm_fpga_mgr_register(dev, mgr);
>> +	return 0;
>>  }
>>  
>>  static struct platform_driver fme_mgr_driver = {
>> diff --git a/drivers/fpga/dfl-fme-region.c b/drivers/fpga/dfl-fme-region.c
>> index 1eeb42af1012..a64c13e198a0 100644
>> --- a/drivers/fpga/dfl-fme-region.c
>> +++ b/drivers/fpga/dfl-fme-region.c
>> @@ -46,7 +46,7 @@ static int fme_region_probe(struct platform_device *pdev)
>>  	}
>>  
>>  	region->priv = pdata;
>> -	region->compat_id = mgr->compat_id;
>> +	region->compat_id = mgr->mops->compat_id;
> The changes should be in Patch #3, is it?
This is the patch that moves compat_id out of mgr and into mgr->mops.
In order to be able to compile, this patch needs the change.

>
>>  	platform_set_drvdata(pdev, region);
>>  
>>  	ret = fpga_region_register(region);
>> diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
>> index ecb4c3c795fa..9aecfea432c8 100644
>> --- a/drivers/fpga/fpga-mgr.c
>> +++ b/drivers/fpga/fpga-mgr.c
>> @@ -550,20 +550,20 @@ void fpga_mgr_unlock(struct fpga_manager *mgr)
>>  EXPORT_SYMBOL_GPL(fpga_mgr_unlock);
>>  
>>  /**
>> - * fpga_mgr_create - create and initialize an FPGA manager struct
>> + * 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
>>   *
>> - * 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 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 *parent, 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)
> fpga_mgr_register(struct device *parent, struct fpga_mgr_info *minfo)
>
>>  {
>>  	struct fpga_manager *mgr;
>>  	int id, ret;
>> @@ -572,21 +572,23 @@ struct fpga_manager *fpga_mgr_create(struct device *parent, const char *name,
>>  	    !mops->write_init || (!mops->write && !mops->write_sg) ||
>>  	    (mops->write && mops->write_sg)) {
>>  		dev_err(parent, "Attempt to register without fpga_manager_ops\n");
>> -		return NULL;
>> +		return ERR_PTR(-EINVAL);
>>  	}
>>  
>>  	if (!name || !strlen(name)) {
>>  		dev_err(parent, "Attempt to register with no name!\n");
>> -		return NULL;
>> +		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)
>> +	if (id < 0) {
>> +		ret = id;
>>  		goto error_kfree;
>> +	}
>>  
>>  	mutex_init(&mgr->ref_mutex);
>>  
>> @@ -594,7 +596,6 @@ struct fpga_manager *fpga_mgr_create(struct device *parent, const char *name,
>>  	mgr->mops = mops;
>>  	mgr->priv = priv;
> 	mgr->name = minfo->name;
> 	mgr->mops = minfo->mops;
> 	mgr->priv = minfo->priv;
> 	mgr->compat_id = minfo->compat_id;
>
>>  
>> -	device_initialize(&mgr->dev);
>>  	mgr->dev.class = fpga_mgr_class;
>>  	mgr->dev.groups = mops->groups;
>>  	mgr->dev.parent = parent;
>> @@ -605,84 +606,6 @@ struct fpga_manager *fpga_mgr_create(struct device *parent, const char *name,
>>  	if (ret)
>>  		goto error_device;
>>  
>> -	return mgr;
>> -
>> -error_device:
>> -	ida_simple_remove(&fpga_mgr_ida, id);
>> -error_kfree:
>> -	kfree(mgr);
>> -
>> -	return NULL;
>> -}
>> -EXPORT_SYMBOL_GPL(fpga_mgr_create);
>> -
>> -/**
>> - * fpga_mgr_free - free an 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_mgr_devres *dr = res;
>> -
>> -	fpga_mgr_free(dr->mgr);
>> -}
>> -
>> -/**
>> - * devm_fpga_mgr_create - create and initialize a managed FPGA manager struct
>> - * @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 an 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.
>> - *
>> - * Return: pointer to struct fpga_manager or NULL
>> - */
>> -struct fpga_manager *devm_fpga_mgr_create(struct device *parent, const char *name,
>> -					  const struct fpga_manager_ops *mops,
>> -					  void *priv)
>> -{
>> -	struct fpga_mgr_devres *dr;
>> -
>> -	dr = devres_alloc(devm_fpga_mgr_release, sizeof(*dr), GFP_KERNEL);
>> -	if (!dr)
>> -		return NULL;
>> -
>> -	dr->mgr = fpga_mgr_create(parent, name, mops, priv);
>> -	if (!dr->mgr) {
>> -		devres_free(dr);
>> -		return NULL;
>> -	}
>> -
>> -	devres_add(parent, dr);
>> -
>> -	return dr->mgr;
>> -}
>> -EXPORT_SYMBOL_GPL(devm_fpga_mgr_create);
>> -
>> -/**
>> - * fpga_mgr_register - register an FPGA manager
>> - * @mgr: fpga manager struct
>> - *
>> - * Return: 0 on success, negative error code otherwise.
>> - */
>> -int fpga_mgr_register(struct fpga_manager *mgr)
>> -{
>> -	int ret;
>> -
>>  	/*
>>  	 * Initialize framework state by requesting low level driver read state
>>  	 * from device.  FPGA may be in reset mode or may have been programmed
>> @@ -690,18 +613,20 @@ int fpga_mgr_register(struct fpga_manager *mgr)
>>  	 */
>>  	mgr->state = mgr->mops->state(mgr);
>>  
>> -	ret = device_add(&mgr->dev);
>> -	if (ret)
>> -		goto error_device;
>> -
>> -	dev_info(&mgr->dev, "%s registered\n", mgr->name);
>> +	ret = device_register(&mgr->dev);
>> +	if (ret) {
>> +		put_device(&mgr->dev);
>> +		return ERR_PTR(ret);
>> +	}
>>  
>> -	return 0;
>> +	return mgr;
>>  
>>  error_device:
>> -	ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
>> +	ida_simple_remove(&fpga_mgr_ida, id);
>> +error_kfree:
>> +	kfree(mgr);
>>  
>> -	return ret;
>> +	return ERR_PTR(ret);
>>  }
>>  EXPORT_SYMBOL_GPL(fpga_mgr_register);
>>  
>> @@ -726,14 +651,6 @@ void fpga_mgr_unregister(struct fpga_manager *mgr)
>>  }
>>  EXPORT_SYMBOL_GPL(fpga_mgr_unregister);
>>  
>> -static int fpga_mgr_devres_match(struct device *dev, void *res,
>> -				 void *match_data)
>> -{
>> -	struct fpga_mgr_devres *dr = res;
>> -
>> -	return match_data == dr->mgr;
>> -}
>> -
>>  static void devm_fpga_mgr_unregister(struct device *dev, void *res)
>>  {
>>  	struct fpga_mgr_devres *dr = res;
>> @@ -743,44 +660,44 @@ static void devm_fpga_mgr_unregister(struct device *dev, void *res)
>>  
>>  /**
>>   * devm_fpga_mgr_register - resource managed variant of fpga_mgr_register()
>> - * @dev: managing device for this FPGA manager
>> - * @mgr: 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
>>   *
>>   * This is the devres variant of fpga_mgr_register() for which the unregister
>>   * function will be called automatically when the managing device is detached.
>>   */
>> -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr)
>> +struct fpga_manager *
>> +devm_fpga_mgr_register(struct device *dev, const char *name,
>    devm_fpga_mgr_register(struct device *parent,
Yes - thanks for catching that.

- Russ

>
>
>> +		       const struct fpga_manager_ops *mops, void *priv)
>>  {
>>  	struct fpga_mgr_devres *dr;
>> -	int ret;
>> -
>> -	/*
>> -	 * Make sure that the struct fpga_manager * that is passed in is
>> -	 * managed itself.
>> -	 */
>> -	if (WARN_ON(!devres_find(dev, devm_fpga_mgr_release,
>> -				 fpga_mgr_devres_match, mgr)))
>> -		return -EINVAL;
>> +	struct fpga_manager *mgr;
>>  
>>  	dr = devres_alloc(devm_fpga_mgr_unregister, sizeof(*dr), GFP_KERNEL);
>>  	if (!dr)
>> -		return -ENOMEM;
>> +		return ERR_PTR(-ENOMEM);
>>  
>> -	ret = fpga_mgr_register(mgr);
>> -	if (ret) {
>> +	mgr = fpga_mgr_register(dev, name, mops, priv);
>> +	if (IS_ERR(mgr)) {
>>  		devres_free(dr);
>> -		return ret;
>> +		return mgr;
>>  	}
>>  
>>  	dr->mgr = mgr;
>>  	devres_add(dev, dr);
>>  
>> -	return 0;
>> +	return mgr;
>>  }
>>  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)
>> diff --git a/drivers/fpga/ice40-spi.c b/drivers/fpga/ice40-spi.c
>> index 69dec5af23c3..91ac25f339f3 100644
>> --- a/drivers/fpga/ice40-spi.c
>> +++ b/drivers/fpga/ice40-spi.c
>> @@ -178,12 +178,12 @@ static int ice40_fpga_probe(struct spi_device *spi)
>>  		return ret;
>>  	}
>>  
>> -	mgr = devm_fpga_mgr_create(dev, "Lattice iCE40 FPGA Manager",
>> -				   &ice40_fpga_ops, priv);
>> -	if (!mgr)
>> -		return -ENOMEM;
>> +	mgr = devm_fpga_mgr_register(dev, "Lattice iCE40 FPGA Manager",
>> +				     &ice40_fpga_ops, priv);
>> +	if (IS_ERR(mgr))
>> +		return PTR_ERR(mgr);
>>  
>> -	return devm_fpga_mgr_register(dev, mgr);
>> +	return 0;
>>  }
>>  
>>  static const struct of_device_id ice40_fpga_of_match[] = {
>> diff --git a/drivers/fpga/machxo2-spi.c b/drivers/fpga/machxo2-spi.c
>> index 114a64d2b7a4..0137c1e2e706 100644
>> --- a/drivers/fpga/machxo2-spi.c
>> +++ b/drivers/fpga/machxo2-spi.c
>> @@ -366,12 +366,12 @@ static int machxo2_spi_probe(struct spi_device *spi)
>>  		return -EINVAL;
>>  	}
>>  
>> -	mgr = devm_fpga_mgr_create(dev, "Lattice MachXO2 SPI FPGA Manager",
>> -				   &machxo2_ops, spi);
>> -	if (!mgr)
>> -		return -ENOMEM;
>> +	mgr = devm_fpga_mgr_register(dev, "Lattice MachXO2 SPI FPGA Manager",
>> +				     &machxo2_ops, spi);
>> +	if (IS_ERR(mgr))
>> +		return PTR_ERR(mgr);
>>  
>> -	return devm_fpga_mgr_register(dev, mgr);
>> +	return 0;
>>  }
>>  
>>  static const struct of_device_id of_match[] = {
>> diff --git a/drivers/fpga/socfpga-a10.c b/drivers/fpga/socfpga-a10.c
>> index 573d88bdf730..ac8e89b8a5cc 100644
>> --- a/drivers/fpga/socfpga-a10.c
>> +++ b/drivers/fpga/socfpga-a10.c
>> @@ -508,19 +508,15 @@ static int socfpga_a10_fpga_probe(struct platform_device *pdev)
>>  		return -EBUSY;
>>  	}
>>  
>> -	mgr = devm_fpga_mgr_create(dev, "SoCFPGA Arria10 FPGA Manager",
>> -				   &socfpga_a10_fpga_mgr_ops, priv);
>> -	if (!mgr)
>> -		return -ENOMEM;
>> -
>> -	platform_set_drvdata(pdev, mgr);
>> -
>> -	ret = fpga_mgr_register(mgr);
>> -	if (ret) {
>> +	mgr = fpga_mgr_register(dev, "SoCFPGA Arria10 FPGA Manager",
>> +				&socfpga_a10_fpga_mgr_ops, priv);
>> +	if (IS_ERR(mgr)) {
>>  		clk_disable_unprepare(priv->clk);
>> -		return ret;
>> +		return PTR_ERR(mgr);
>>  	}
>>  
>> +	platform_set_drvdata(pdev, mgr);
>> +
>>  	return 0;
>>  }
>>  
>> diff --git a/drivers/fpga/socfpga.c b/drivers/fpga/socfpga.c
>> index 1f467173fc1f..ebfa9f4d24ef 100644
>> --- a/drivers/fpga/socfpga.c
>> +++ b/drivers/fpga/socfpga.c
>> @@ -571,12 +571,12 @@ static int socfpga_fpga_probe(struct platform_device *pdev)
>>  	if (ret)
>>  		return ret;
>>  
>> -	mgr = devm_fpga_mgr_create(dev, "Altera SOCFPGA FPGA Manager",
>> -				   &socfpga_fpga_ops, priv);
>> -	if (!mgr)
>> -		return -ENOMEM;
>> +	mgr = devm_fpga_mgr_register(dev, "Altera SOCFPGA FPGA Manager",
>> +				     &socfpga_fpga_ops, priv);
>> +	if (IS_ERR(mgr))
>> +		return PTR_ERR(mgr);
>>  
>> -	return devm_fpga_mgr_register(dev, mgr);
>> +	return 0;
>>  }
>>  
>>  #ifdef CONFIG_OF
>> diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c
>> index a2cea500f7cc..9155e888a133 100644
>> --- a/drivers/fpga/stratix10-soc.c
>> +++ b/drivers/fpga/stratix10-soc.c
>> @@ -425,18 +425,11 @@ static int s10_probe(struct platform_device *pdev)
>>  
>>  	init_completion(&priv->status_return_completion);
>>  
>> -	mgr = fpga_mgr_create(dev, "Stratix10 SOC FPGA Manager",
>> -			      &s10_ops, priv);
>> -	if (!mgr) {
>> -		dev_err(dev, "unable to create FPGA manager\n");
>> -		ret = -ENOMEM;
>> -		goto probe_err;
>> -	}
>> -
>> -	ret = fpga_mgr_register(mgr);
>> -	if (ret) {
>> +	mgr = fpga_mgr_register(dev, "Stratix10 SOC FPGA Manager",
>> +				&s10_ops, priv);
>> +	if (IS_ERR(mgr)) {
>>  		dev_err(dev, "unable to register FPGA manager\n");
>> -		fpga_mgr_free(mgr);
>> +		ret = PTR_ERR(mgr);
>>  		goto probe_err;
>>  	}
>>  
>> @@ -454,7 +447,6 @@ static int s10_remove(struct platform_device *pdev)
>>  	struct s10_priv *priv = mgr->priv;
>>  
>>  	fpga_mgr_unregister(mgr);
>> -	fpga_mgr_free(mgr);
>>  	stratix10_svc_free_channel(priv->chan);
>>  
>>  	return 0;
>> diff --git a/drivers/fpga/ts73xx-fpga.c b/drivers/fpga/ts73xx-fpga.c
>> index 101f016c6ed8..5efbec2b9f9f 100644
>> --- a/drivers/fpga/ts73xx-fpga.c
>> +++ b/drivers/fpga/ts73xx-fpga.c
>> @@ -122,12 +122,12 @@ static int ts73xx_fpga_probe(struct platform_device *pdev)
>>  	if (IS_ERR(priv->io_base))
>>  		return PTR_ERR(priv->io_base);
>>  
>> -	mgr = devm_fpga_mgr_create(kdev, "TS-73xx FPGA Manager",
>> -				   &ts73xx_fpga_ops, priv);
>> -	if (!mgr)
>> -		return -ENOMEM;
>> +	mgr = devm_fpga_mgr_register(kdev, "TS-73xx FPGA Manager",
>> +				     &ts73xx_fpga_ops, priv);
>> +	if (IS_ERR(mgr))
>> +		return PTR_ERR(mgr);
>>  
>> -	return devm_fpga_mgr_register(kdev, mgr);
>> +	return 0;
>>  }
>>  
>>  static struct platform_driver ts73xx_fpga_driver = {
>> diff --git a/drivers/fpga/xilinx-spi.c b/drivers/fpga/xilinx-spi.c
>> index fee4d0abf6bf..efda475f7a98 100644
>> --- a/drivers/fpga/xilinx-spi.c
>> +++ b/drivers/fpga/xilinx-spi.c
>> @@ -247,13 +247,13 @@ static int xilinx_spi_probe(struct spi_device *spi)
>>  		return dev_err_probe(&spi->dev, PTR_ERR(conf->done),
>>  				     "Failed to get DONE gpio\n");
>>  
>> -	mgr = devm_fpga_mgr_create(&spi->dev,
>> -				   "Xilinx Slave Serial FPGA Manager",
>> -				   &xilinx_spi_ops, conf);
>> -	if (!mgr)
>> -		return -ENOMEM;
>> +	mgr = devm_fpga_mgr_register(&spi->dev,
>> +				     "Xilinx Slave Serial FPGA Manager",
>> +				     &xilinx_spi_ops, conf);
>> +	if (IS_ERR(mgr))
>> +		return PTR_ERR(mgr);
>>  
>> -	return devm_fpga_mgr_register(&spi->dev, mgr);
>> +	return 0;
>>  }
>>  
>>  static const struct of_device_id xlnx_spi_of_match[] = {
>> diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c
>> index 9b75bd4f93d8..426aa34c6a0d 100644
>> --- a/drivers/fpga/zynq-fpga.c
>> +++ b/drivers/fpga/zynq-fpga.c
>> @@ -609,20 +609,16 @@ static int zynq_fpga_probe(struct platform_device *pdev)
>>  
>>  	clk_disable(priv->clk);
>>  
>> -	mgr = devm_fpga_mgr_create(dev, "Xilinx Zynq FPGA Manager",
>> -				   &zynq_fpga_ops, priv);
>> -	if (!mgr)
>> -		return -ENOMEM;
>> -
>> -	platform_set_drvdata(pdev, mgr);
>> -
>> -	err = fpga_mgr_register(mgr);
>> -	if (err) {
>> +	mgr = fpga_mgr_register(dev, "Xilinx Zynq FPGA Manager",
>> +				&zynq_fpga_ops, priv);
>> +	if (IS_ERR(mgr)) {
>>  		dev_err(dev, "unable to register FPGA manager\n");
>>  		clk_unprepare(priv->clk);
>> -		return err;
>> +		return PTR_ERR(mgr);
>>  	}
>>  
>> +	platform_set_drvdata(pdev, mgr);
>> +
>>  	return 0;
>>  }
>>  
>> diff --git a/drivers/fpga/zynqmp-fpga.c b/drivers/fpga/zynqmp-fpga.c
>> index 125743c9797f..1a6d520985be 100644
>> --- a/drivers/fpga/zynqmp-fpga.c
>> +++ b/drivers/fpga/zynqmp-fpga.c
>> @@ -102,12 +102,12 @@ static int zynqmp_fpga_probe(struct platform_device *pdev)
>>  
>>  	priv->dev = dev;
>>  
>> -	mgr = devm_fpga_mgr_create(dev, "Xilinx ZynqMP FPGA Manager",
>> -				   &zynqmp_fpga_ops, priv);
>> -	if (!mgr)
>> -		return -ENOMEM;
>> +	mgr = devm_fpga_mgr_register(dev, "Xilinx ZynqMP FPGA Manager",
>> +				     &zynqmp_fpga_ops, priv);
>> +	if (IS_ERR(mgr))
>> +		return PTR_ERR(mgr);
>>  
>> -	return devm_fpga_mgr_register(dev, mgr);
>> +	return 0;
>>  }
>>  
>>  static const struct of_device_id zynqmp_fpga_of_match[] = {
>> diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
>> index 474c1f506307..d3c6f0153457 100644
>> --- a/include/linux/fpga/fpga-mgr.h
>> +++ b/include/linux/fpga/fpga-mgr.h
>> @@ -105,6 +105,17 @@ struct fpga_image_info {
>>  #endif
>>  };
>>  
>> +/**
>> + * struct fpga_compat_id - id for compatibility check
>> + *
>> + * @id_h: high 64bit of the compat_id
>> + * @id_l: low 64bit of the compat_id
>> + */
>> +struct fpga_compat_id {
>> +	u64 id_h;
>> +	u64 id_l;
>> +};
>> +
>>  /**
>>   * struct fpga_manager_ops - ops for low level fpga manager drivers
>>   * @initial_header_size: Maximum number of bytes that should be passed into write_init
>> @@ -116,6 +127,7 @@ struct fpga_image_info {
>>   * @write_complete: set FPGA to operating state after writing is done
>>   * @fpga_remove: optional: Set FPGA into a specific state during driver remove
>>   * @groups: optional attribute groups.
>> + * @compat_id: optional: FPGA manager id for compatibility check.
>>   *
>>   * fpga_manager_ops are the low level functions implemented by a specific
>>   * fpga manager driver.  The optional ones are tested for NULL before being
>> @@ -134,6 +146,7 @@ struct fpga_manager_ops {
>>  			      struct fpga_image_info *info);
>>  	void (*fpga_remove)(struct fpga_manager *mgr);
>>  	const struct attribute_group **groups;
>> +	struct fpga_compat_id *compat_id;
> We don't have to move compat_id here, keep it in struct fpga_mgr
>
> Thanks,
> Yilun
>
>>  };
>>  
>>  /* FPGA manager status: Partial/Full Reconfiguration errors */
>> @@ -143,24 +156,12 @@ struct fpga_manager_ops {
>>  #define FPGA_MGR_STATUS_IP_PROTOCOL_ERR		BIT(3)
>>  #define FPGA_MGR_STATUS_FIFO_OVERFLOW_ERR	BIT(4)
>>  
>> -/**
>> - * struct fpga_compat_id - id for compatibility check
>> - *
>> - * @id_h: high 64bit of the compat_id
>> - * @id_l: low 64bit of the compat_id
>> - */
>> -struct fpga_compat_id {
>> -	u64 id_h;
>> -	u64 id_l;
>> -};
>> -
>>  /**
>>   * struct fpga_manager - fpga manager structure
>>   * @name: name of low level fpga manager
>>   * @dev: fpga manager device
>>   * @ref_mutex: only allows one reference to fpga manager
>>   * @state: state of fpga manager
>> - * @compat_id: FPGA manager id for compatibility check.
>>   * @mops: pointer to struct of fpga manager ops
>>   * @priv: low level driver private date
>>   */
>> @@ -169,7 +170,6 @@ struct fpga_manager {
>>  	struct device dev;
>>  	struct mutex ref_mutex;
>>  	enum fpga_mgr_states state;
>> -	struct fpga_compat_id *compat_id;
>>  	const struct fpga_manager_ops *mops;
>>  	void *priv;
>>  };
>> @@ -191,17 +191,14 @@ struct fpga_manager *fpga_mgr_get(struct device *dev);
>>  
>>  void fpga_mgr_put(struct fpga_manager *mgr);
>>  
>> -struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
>> -				     const struct fpga_manager_ops *mops,
>> -				     void *priv);
>> -void fpga_mgr_free(struct fpga_manager *mgr);
>> -int fpga_mgr_register(struct fpga_manager *mgr);
>> +struct fpga_manager *fpga_mgr_register(struct device *dev, const char *name,
>> +				       const struct fpga_manager_ops *mops,
>> +				       void *priv);
>>  void fpga_mgr_unregister(struct fpga_manager *mgr);
>>  
>> -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr);
>> -
>> -struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name,
>> -					  const struct fpga_manager_ops *mops,
>> -					  void *priv);
>> +struct fpga_manager *
>> +devm_fpga_mgr_register(struct device *dev, const char *name,
>> +		       const struct fpga_manager_ops *mops,
>> +		       void *priv);
>>  
>>  #endif /*_LINUX_FPGA_MGR_H */
>> -- 
>> 2.25.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v5 2/3] fpga: bridge: Use standard dev_release for class driver
  2021-06-18 15:52   ` Xu Yilun
@ 2021-06-18 16:05     ` Russ Weight
  2021-06-21  7:38       ` Xu Yilun
  0 siblings, 1 reply; 19+ messages in thread
From: Russ Weight @ 2021-06-18 16:05 UTC (permalink / raw)
  To: Xu Yilun
  Cc: mdf, linux-fpga, trix, lgoncalv, hao.wu, matthew.gerlach, richard.gong



On 6/18/21 8:52 AM, Xu Yilun wrote:
> On Wed, Jun 16, 2021 at 03:57:39PM -0700, Russ Weight wrote:
>> The FPGA bridge class driver data structure is being treated as a
>> managed resource instead of using standard dev_release call-back
>> to release the class data structure. This change removes the
>> managed resource code and combines the create() and register()
>> functions into a single register() function.
>>
>> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
>> ---
>> v5:
>>   - Rebased on top of recently accepted patches.
>> v4:
>>   - Restore the previous format for the Return value in the comment header
>>     for fpga_bridge_register()
>> v3:
>>   - Cleaned up comment header for fpga_bridge_register()
>>   - Fix error return values for fpga_bridge_register()
>> v2:
>>   - No changes
>> ---
>>  drivers/fpga/altera-fpga2sdram.c    |  12 +--
>>  drivers/fpga/altera-freeze-bridge.c |  10 +--
>>  drivers/fpga/altera-hps2fpga.c      |  12 +--
>>  drivers/fpga/dfl-fme-br.c           |  10 +--
>>  drivers/fpga/fpga-bridge.c          | 116 ++++++----------------------
>>  drivers/fpga/xilinx-pr-decoupler.c  |  17 ++--
>>  include/linux/fpga/fpga-bridge.h    |  12 +--
>>  7 files changed, 49 insertions(+), 140 deletions(-)
>>
>> diff --git a/drivers/fpga/altera-fpga2sdram.c b/drivers/fpga/altera-fpga2sdram.c
>> index a78e49c63c64..66063507116b 100644
>> --- a/drivers/fpga/altera-fpga2sdram.c
>> +++ b/drivers/fpga/altera-fpga2sdram.c
>> @@ -121,17 +121,13 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev)
>>  	/* Get f2s bridge configuration saved in handoff register */
>>  	regmap_read(sysmgr, SYSMGR_ISWGRP_HANDOFF3, &priv->mask);
>>  
>> -	br = devm_fpga_bridge_create(dev, F2S_BRIDGE_NAME,
>> -				     &altera_fpga2sdram_br_ops, priv);
>> -	if (!br)
>> -		return -ENOMEM;
>> +	br = fpga_bridge_register(dev, F2S_BRIDGE_NAME,
>> +				  &altera_fpga2sdram_br_ops, priv);
>> +	if (IS_ERR(br))
>> +		return PTR_ERR(mgr);
>>  
>>  	platform_set_drvdata(pdev, br);
>>  
>> -	ret = fpga_bridge_register(br);
>> -	if (ret)
>> -		return ret;
>> -
>>  	dev_info(dev, "driver initialized with handoff %08x\n", priv->mask);
>>  
>>  	if (!of_property_read_u32(dev->of_node, "bridge-enable", &enable)) {
>> diff --git a/drivers/fpga/altera-freeze-bridge.c b/drivers/fpga/altera-freeze-bridge.c
>> index dd58c4aea92e..bfbfa43cd05b 100644
>> --- a/drivers/fpga/altera-freeze-bridge.c
>> +++ b/drivers/fpga/altera-freeze-bridge.c
>> @@ -244,14 +244,14 @@ static int altera_freeze_br_probe(struct platform_device *pdev)
>>  
>>  	priv->base_addr = base_addr;
>>  
>> -	br = devm_fpga_bridge_create(dev, FREEZE_BRIDGE_NAME,
>> -				     &altera_freeze_br_br_ops, priv);
>> -	if (!br)
>> -		return -ENOMEM;
>> +	br = fpga_bridge_register(dev, FREEZE_BRIDGE_NAME,
>> +				  &altera_freeze_br_br_ops, priv);
>> +	if (IS_ERR(br))
>> +		return PTR_ERR(br);
>>  
>>  	platform_set_drvdata(pdev, br);
>>  
>> -	return fpga_bridge_register(br);
>> +	return 0;
>>  }
>>  
>>  static int altera_freeze_br_remove(struct platform_device *pdev)
>> diff --git a/drivers/fpga/altera-hps2fpga.c b/drivers/fpga/altera-hps2fpga.c
>> index 77b95f251821..aa758426c22b 100644
>> --- a/drivers/fpga/altera-hps2fpga.c
>> +++ b/drivers/fpga/altera-hps2fpga.c
>> @@ -180,19 +180,15 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev)
>>  		}
>>  	}
>>  
>> -	br = devm_fpga_bridge_create(dev, priv->name,
>> -				     &altera_hps2fpga_br_ops, priv);
>> -	if (!br) {
>> -		ret = -ENOMEM;
>> +	br = fpga_bridge_register(dev, priv->name,
>> +				  &altera_hps2fpga_br_ops, priv);
>> +	if (IS_ERR(br)) {
>> +		ret = PTR_ERR(br);
>>  		goto err;
>>  	}
>>  
>>  	platform_set_drvdata(pdev, br);
>>  
>> -	ret = fpga_bridge_register(br);
>> -	if (ret)
>> -		goto err;
>> -
>>  	return 0;
>>  
>>  err:
>> diff --git a/drivers/fpga/dfl-fme-br.c b/drivers/fpga/dfl-fme-br.c
>> index 3ff9f3a687ce..808d1f4d76df 100644
>> --- a/drivers/fpga/dfl-fme-br.c
>> +++ b/drivers/fpga/dfl-fme-br.c
>> @@ -68,14 +68,14 @@ static int fme_br_probe(struct platform_device *pdev)
>>  
>>  	priv->pdata = dev_get_platdata(dev);
>>  
>> -	br = devm_fpga_bridge_create(dev, "DFL FPGA FME Bridge",
>> -				     &fme_bridge_ops, priv);
>> -	if (!br)
>> -		return -ENOMEM;
>> +	br = fpga_bridge_register(dev, "DFL FPGA FME Bridge",
>> +				  &fme_bridge_ops, priv);
>> +	if (IS_ERR(br))
>> +		return PTR_ERR(br);
>>  
>>  	platform_set_drvdata(pdev, br);
>>  
>> -	return fpga_bridge_register(br);
>> +	return 0;
>>  }
>>  
>>  static int fme_br_remove(struct platform_device *pdev)
>> diff --git a/drivers/fpga/fpga-bridge.c b/drivers/fpga/fpga-bridge.c
>> index 798f55670646..15be378b64ab 100644
>> --- a/drivers/fpga/fpga-bridge.c
>> +++ b/drivers/fpga/fpga-bridge.c
>> @@ -312,36 +312,35 @@ static struct attribute *fpga_bridge_attrs[] = {
>>  ATTRIBUTE_GROUPS(fpga_bridge);
>>  
>>  /**
>> - * fpga_bridge_create - create and initialize a struct fpga_bridge
>> + * fpga_bridge_register - create and register a FPGA Bridge device
>>   * @parent:	FPGA bridge device from pdev
>>   * @name:	FPGA bridge name
>>   * @br_ops:	pointer to structure of fpga bridge ops
>>   * @priv:	FPGA bridge private data
>>   *
>> - * The caller of this function is responsible for freeing the bridge with
>> - * fpga_bridge_free().  Using devm_fpga_bridge_create() instead is recommended.
>> - *
>> - * Return: struct fpga_bridge or NULL
>> + * Return: struct fpga_bridge pointer or ERR_PTR()
>>   */
>> -struct fpga_bridge *fpga_bridge_create(struct device *parent, const char *name,
>> -				       const struct fpga_bridge_ops *br_ops,
>> -				       void *priv)
>> +struct fpga_bridge *fpga_bridge_register(struct device *parent, const char *name,
>> +					 const struct fpga_bridge_ops *br_ops,
>> +					 void *priv)
> Is it better we use the same style as fpga_mgr_register?

Can you explain? Do you mean the suggestions you are making for passing
a new info structure?

>
> Thanks,
> Yilun
>
>>  {
>>  	struct fpga_bridge *bridge;
>>  	int id, ret;
>>  
>>  	if (!name || !strlen(name)) {
>>  		dev_err(parent, "Attempt to register with no name!\n");
>> -		return NULL;
>> +		return ERR_PTR(-EINVAL);
>>  	}
>>  
>>  	bridge = kzalloc(sizeof(*bridge), GFP_KERNEL);
>>  	if (!bridge)
>> -		return NULL;
>> +		return ERR_PTR(-ENOMEM);
>>  
>>  	id = ida_simple_get(&fpga_bridge_ida, 0, 0, GFP_KERNEL);
>> -	if (id < 0)
>> +	if (id < 0) {
>> +		ret = id;
>>  		goto error_kfree;
>> +	}
>>  
>>  	mutex_init(&bridge->mutex);
>>  	INIT_LIST_HEAD(&bridge->node);
>> @@ -350,17 +349,23 @@ struct fpga_bridge *fpga_bridge_create(struct device *parent, const char *name,
>>  	bridge->br_ops = br_ops;
>>  	bridge->priv = priv;
>>  
>> -	device_initialize(&bridge->dev);
>>  	bridge->dev.groups = br_ops->groups;
>>  	bridge->dev.class = fpga_bridge_class;
>>  	bridge->dev.parent = parent;
>>  	bridge->dev.of_node = parent->of_node;
>>  	bridge->dev.id = id;
>> +	of_platform_populate(bridge->dev.of_node, NULL, NULL, &bridge->dev);
>>  
>>  	ret = dev_set_name(&bridge->dev, "br%d", id);
>>  	if (ret)
>>  		goto error_device;
>>  
>> +	ret = device_register(&bridge->dev);
>> +	if (ret) {
>> +		put_device(&bridge->dev);
>> +		return ERR_PTR(ret);
>> +	}
>> +
>>  	return bridge;
>>  
>>  error_device:
>> @@ -368,88 +373,7 @@ struct fpga_bridge *fpga_bridge_create(struct device *parent, const char *name,
>>  error_kfree:
>>  	kfree(bridge);
>>  
>> -	return NULL;
>> -}
>> -EXPORT_SYMBOL_GPL(fpga_bridge_create);
>> -
>> -/**
>> - * fpga_bridge_free - free an fpga bridge created by fpga_bridge_create()
>> - * @bridge:	FPGA bridge struct
>> - */
>> -void fpga_bridge_free(struct fpga_bridge *bridge)
>> -{
>> -	ida_simple_remove(&fpga_bridge_ida, bridge->dev.id);
>> -	kfree(bridge);
>> -}
>> -EXPORT_SYMBOL_GPL(fpga_bridge_free);
>> -
>> -static void devm_fpga_bridge_release(struct device *dev, void *res)
>> -{
>> -	struct fpga_bridge *bridge = *(struct fpga_bridge **)res;
>> -
>> -	fpga_bridge_free(bridge);
>> -}
>> -
>> -/**
>> - * devm_fpga_bridge_create - create and init a managed struct fpga_bridge
>> - * @parent:	FPGA bridge device from pdev
>> - * @name:	FPGA bridge name
>> - * @br_ops:	pointer to structure of fpga bridge ops
>> - * @priv:	FPGA bridge private data
>> - *
>> - * This function is intended for use in an FPGA bridge driver's probe function.
>> - * After the bridge driver creates the struct with devm_fpga_bridge_create(), it
>> - * should register the bridge with fpga_bridge_register().  The bridge driver's
>> - * remove function should call fpga_bridge_unregister().  The bridge 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_bridge_register(), the struct will still get cleaned up.
>> - *
>> - *  Return: struct fpga_bridge or NULL
>> - */
>> -struct fpga_bridge
>> -*devm_fpga_bridge_create(struct device *parent, const char *name,
>> -			 const struct fpga_bridge_ops *br_ops, void *priv)
>> -{
>> -	struct fpga_bridge **ptr, *bridge;
>> -
>> -	ptr = devres_alloc(devm_fpga_bridge_release, sizeof(*ptr), GFP_KERNEL);
>> -	if (!ptr)
>> -		return NULL;
>> -
>> -	bridge = fpga_bridge_create(parent, name, br_ops, priv);
>> -	if (!bridge) {
>> -		devres_free(ptr);
>> -	} else {
>> -		*ptr = bridge;
>> -		devres_add(parent, ptr);
>> -	}
>> -
>> -	return bridge;
>> -}
>> -EXPORT_SYMBOL_GPL(devm_fpga_bridge_create);
>> -
>> -/**
>> - * fpga_bridge_register - register an FPGA bridge
>> - *
>> - * @bridge: FPGA bridge struct
>> - *
>> - * Return: 0 for success, error code otherwise.
>> - */
>> -int fpga_bridge_register(struct fpga_bridge *bridge)
>> -{
>> -	struct device *dev = &bridge->dev;
>> -	int ret;
>> -
>> -	ret = device_add(dev);
>> -	if (ret)
>> -		return ret;
>> -
>> -	of_platform_populate(dev->of_node, NULL, NULL, dev);
>> -
>> -	dev_info(dev->parent, "fpga bridge [%s] registered\n", bridge->name);
>> -
>> -	return 0;
>> +	return ERR_PTR(ret);
>>  }
>>  EXPORT_SYMBOL_GPL(fpga_bridge_register);
>>  
>> @@ -475,6 +399,10 @@ EXPORT_SYMBOL_GPL(fpga_bridge_unregister);
>>  
>>  static void fpga_bridge_dev_release(struct device *dev)
>>  {
>> +	struct fpga_bridge *bridge = to_fpga_bridge(dev);
>> +
>> +	ida_simple_remove(&fpga_bridge_ida, bridge->dev.id);
>> +	kfree(bridge);
>>  }
>>  
>>  static int __init fpga_bridge_dev_init(void)
>> diff --git a/drivers/fpga/xilinx-pr-decoupler.c b/drivers/fpga/xilinx-pr-decoupler.c
>> index ea2bde6e5bc4..c004e52b9464 100644
>> --- a/drivers/fpga/xilinx-pr-decoupler.c
>> +++ b/drivers/fpga/xilinx-pr-decoupler.c
>> @@ -138,22 +138,17 @@ static int xlnx_pr_decoupler_probe(struct platform_device *pdev)
>>  
>>  	clk_disable(priv->clk);
>>  
>> -	br = devm_fpga_bridge_create(&pdev->dev, priv->ipconfig->name,
>> -				     &xlnx_pr_decoupler_br_ops, priv);
>> -	if (!br) {
>> -		err = -ENOMEM;
>> -		goto err_clk;
>> -	}
>> -
>> -	platform_set_drvdata(pdev, br);
>> -
>> -	err = fpga_bridge_register(br);
>> -	if (err) {
>> +	br = fpga_bridge_register(&pdev->dev, priv->ipconfig->name,
>> +				  &xlnx_pr_decoupler_br_ops, priv);
>> +	if (IS_ERR(br)) {
>> +		err = PTR_ERR(br);
>>  		dev_err(&pdev->dev, "unable to register %s",
>>  			priv->ipconfig->name);
>>  		goto err_clk;
>>  	}
>>  
>> +	platform_set_drvdata(pdev, br);
>> +
>>  	return 0;
>>  
>>  err_clk:
>> diff --git a/include/linux/fpga/fpga-bridge.h b/include/linux/fpga/fpga-bridge.h
>> index 6c3c28806ff1..2f0eb62c49f5 100644
>> --- a/include/linux/fpga/fpga-bridge.h
>> +++ b/include/linux/fpga/fpga-bridge.h
>> @@ -62,15 +62,9 @@ int of_fpga_bridge_get_to_list(struct device_node *np,
>>  			       struct fpga_image_info *info,
>>  			       struct list_head *bridge_list);
>>  
>> -struct fpga_bridge *fpga_bridge_create(struct device *dev, const char *name,
>> -				       const struct fpga_bridge_ops *br_ops,
>> -				       void *priv);
>> -void fpga_bridge_free(struct fpga_bridge *br);
>> -int fpga_bridge_register(struct fpga_bridge *br);
>> +struct fpga_bridge *fpga_bridge_register(struct device *dev, const char *name,
>> +					 const struct fpga_bridge_ops *br_ops,
>> +					 void *priv);
>>  void fpga_bridge_unregister(struct fpga_bridge *br);
>>  
>> -struct fpga_bridge
>> -*devm_fpga_bridge_create(struct device *dev, const char *name,
>> -			 const struct fpga_bridge_ops *br_ops, void *priv);
>> -
>>  #endif /* _LINUX_FPGA_BRIDGE_H */
>> -- 
>> 2.25.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v5 3/3] fpga: region: Use standard dev_release for class driver
  2021-06-18 15:54   ` Xu Yilun
@ 2021-06-18 16:06     ` Russ Weight
  0 siblings, 0 replies; 19+ messages in thread
From: Russ Weight @ 2021-06-18 16:06 UTC (permalink / raw)
  To: Xu Yilun
  Cc: mdf, linux-fpga, trix, lgoncalv, hao.wu, matthew.gerlach, richard.gong



On 6/18/21 8:54 AM, Xu Yilun wrote:
> On Wed, Jun 16, 2021 at 03:57:40PM -0700, Russ Weight wrote:
>> The FPGA region class driver data structure is being treated as a
>> managed resource instead of using standard dev_release call-back
>> to release the class data structure. This change removes the
>> managed resource code and combines the create() and register()
>> functions into a single register() function.
>>
>> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
>> ---
>> v5:
>>   - Rebased on top of recently accepted patches.
>>   - Created the fpga_region_ops data structure which is optionally passed
>>     to fpga_region_register(). compat_id, the get_bridges() pointer, and
>>     the priv pointer are included in the fpga_region_ops structure.
>> v4:
>>   - Added the compat_id parameter to fpga_region_register() to ensure
>>     that the compat_id is set before the device_register() call.
>>   - Modified the dfl_fpga_feature_devs_enumerate() function to restore
>>     the fpga_region_register() call to the correct location.
>> v3:
>>   - Cleaned up comment header for fpga_region_register()
>>   - Fix fpga_region_register() error return on ida_simple_get() failure
>> v2:
>>   - No changes
>> ---
>>  drivers/fpga/dfl-fme-pr.c        |   2 +-
>>  drivers/fpga/dfl-fme-region.c    |  32 ++++++---
>>  drivers/fpga/dfl.c               |  12 ++--
>>  drivers/fpga/fpga-bridge.c       |   2 +-
>>  drivers/fpga/fpga-region.c       | 119 ++++++++-----------------------
>>  drivers/fpga/of-fpga-region.c    |  14 ++--
>>  include/linux/fpga/fpga-region.h |  38 ++++++----
>>  7 files changed, 86 insertions(+), 133 deletions(-)
>>
>> diff --git a/drivers/fpga/dfl-fme-pr.c b/drivers/fpga/dfl-fme-pr.c
>> index d61ce9a18879..4805d8c533d4 100644
>> --- a/drivers/fpga/dfl-fme-pr.c
>> +++ b/drivers/fpga/dfl-fme-pr.c
>> @@ -151,7 +151,7 @@ static int fme_pr(struct platform_device *pdev, unsigned long arg)
>>  	 * reenabling the bridge to clear things out between acceleration runs.
>>  	 * so no need to hold the bridges after partial reconfiguration.
>>  	 */
>> -	if (region->get_bridges)
>> +	if (region->rops && region->rops->get_bridges)
>>  		fpga_bridges_put(&region->bridge_list);
>>  
>>  	put_device(&region->dev);
>> diff --git a/drivers/fpga/dfl-fme-region.c b/drivers/fpga/dfl-fme-region.c
>> index a64c13e198a0..16d8b3f6f07a 100644
>> --- a/drivers/fpga/dfl-fme-region.c
>> +++ b/drivers/fpga/dfl-fme-region.c
>> @@ -21,12 +21,29 @@
>>  
>>  static int fme_region_get_bridges(struct fpga_region *region)
>>  {
>> -	struct dfl_fme_region_pdata *pdata = region->priv;
>> +	struct dfl_fme_region_pdata *pdata = region->rops->priv;
>>  	struct device *dev = &pdata->br->dev;
>>  
>>  	return fpga_bridge_get_to_list(dev, region->info, &region->bridge_list);
>>  }
>>  
>> +static struct fpga_region_ops *
>> +fme_region_get_ops(struct device *dev, struct fpga_manager *mgr,
>> +		   struct dfl_fme_region_pdata *pdata)
>> +{
>> +	struct fpga_region_ops *ops;
>> +
>> +	ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL);
>> +	if (!ops)
>> +		return NULL;
>> +
>> +	ops->get_bridges = fme_region_get_bridges;
>> +	ops->compat_id = mgr->mops->compat_id;
>> +	ops->priv = pdata;
>> +
>> +	return ops;
>> +}
>> +
>>  static int fme_region_probe(struct platform_device *pdev)
>>  {
>>  	struct dfl_fme_region_pdata *pdata = dev_get_platdata(&pdev->dev);
>> @@ -39,20 +56,15 @@ static int fme_region_probe(struct platform_device *pdev)
>>  	if (IS_ERR(mgr))
>>  		return -EPROBE_DEFER;
>>  
>> -	region = devm_fpga_region_create(dev, mgr, fme_region_get_bridges);
>> -	if (!region) {
>> -		ret = -ENOMEM;
>> +	region = fpga_region_register(dev, mgr,
>> +				      fme_region_get_ops(dev, mgr, pdata));
>> +	if (IS_ERR(region)) {
>> +		ret = PTR_ERR(region);
>>  		goto eprobe_mgr_put;
>>  	}
>>  
>> -	region->priv = pdata;
>> -	region->compat_id = mgr->mops->compat_id;
>>  	platform_set_drvdata(pdev, region);
>>  
>> -	ret = fpga_region_register(region);
>> -	if (ret)
>> -		goto eprobe_mgr_put;
>> -
>>  	dev_dbg(dev, "DFL FME FPGA Region probed\n");
>>  
>>  	return 0;
>> diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c
>> index 511b20ff35a3..3905995ef6d5 100644
>> --- a/drivers/fpga/dfl.c
>> +++ b/drivers/fpga/dfl.c
>> @@ -1400,19 +1400,15 @@ dfl_fpga_feature_devs_enumerate(struct dfl_fpga_enum_info *info)
>>  	if (!cdev)
>>  		return ERR_PTR(-ENOMEM);
>>  
>> -	cdev->region = devm_fpga_region_create(info->dev, NULL, NULL);
>> -	if (!cdev->region) {
>> -		ret = -ENOMEM;
>> -		goto free_cdev_exit;
>> -	}
>> -
>>  	cdev->parent = info->dev;
>>  	mutex_init(&cdev->lock);
>>  	INIT_LIST_HEAD(&cdev->port_dev_list);
>>  
>> -	ret = fpga_region_register(cdev->region);
>> -	if (ret)
>> +	cdev->region = fpga_region_register(info->dev, NULL, NULL);
>> +	if (IS_ERR(cdev->region)) {
>> +		ret = PTR_ERR(cdev->region);
>>  		goto free_cdev_exit;
>> +	}
>>  
>>  	/* create and init build info for enumeration */
>>  	binfo = devm_kzalloc(info->dev, sizeof(*binfo), GFP_KERNEL);
>> diff --git a/drivers/fpga/fpga-bridge.c b/drivers/fpga/fpga-bridge.c
>> index 15be378b64ab..f7e8500561f4 100644
>> --- a/drivers/fpga/fpga-bridge.c
>> +++ b/drivers/fpga/fpga-bridge.c
>> @@ -312,7 +312,7 @@ static struct attribute *fpga_bridge_attrs[] = {
>>  ATTRIBUTE_GROUPS(fpga_bridge);
>>  
>>  /**
>> - * fpga_bridge_register - create and register a FPGA Bridge device
>> + * fpga_bridge_register - create and register an FPGA Bridge device
>>   * @parent:	FPGA bridge device from pdev
>>   * @name:	FPGA bridge name
>>   * @br_ops:	pointer to structure of fpga bridge ops
>> diff --git a/drivers/fpga/fpga-region.c b/drivers/fpga/fpga-region.c
>> index a4838715221f..7199ad93814f 100644
>> --- a/drivers/fpga/fpga-region.c
>> +++ b/drivers/fpga/fpga-region.c
>> @@ -115,8 +115,8 @@ int fpga_region_program_fpga(struct fpga_region *region)
>>  	 * In some cases, we already have a list of bridges in the
>>  	 * fpga region struct.  Or we don't have any bridges.
>>  	 */
>> -	if (region->get_bridges) {
>> -		ret = region->get_bridges(region);
>> +	if (region->rops && region->rops->get_bridges) {
>> +		ret = region->rops->get_bridges(region);
>>  		if (ret) {
>>  			dev_err(dev, "failed to get fpga region bridges\n");
>>  			goto err_unlock_mgr;
>> @@ -147,7 +147,7 @@ int fpga_region_program_fpga(struct fpga_region *region)
>>  	return 0;
>>  
>>  err_put_br:
>> -	if (region->get_bridges)
>> +	if (region->rops && region->rops->get_bridges)
>>  		fpga_bridges_put(&region->bridge_list);
>>  err_unlock_mgr:
>>  	fpga_mgr_unlock(region->mgr);
>> @@ -163,12 +163,12 @@ static ssize_t compat_id_show(struct device *dev,
>>  {
>>  	struct fpga_region *region = to_fpga_region(dev);
>>  
>> -	if (!region->compat_id)
>> +	if (!region->rops || !region->rops->compat_id)
>>  		return -ENOENT;
>>  
>>  	return sprintf(buf, "%016llx%016llx\n",
>> -		       (unsigned long long)region->compat_id->id_h,
>> -		       (unsigned long long)region->compat_id->id_l);
>> +		       (unsigned long long)region->rops->compat_id->id_h,
>> +		       (unsigned long long)region->rops->compat_id->id_l);
>>  }
>>  
>>  static DEVICE_ATTR_RO(compat_id);
>> @@ -180,39 +180,35 @@ static struct attribute *fpga_region_attrs[] = {
>>  ATTRIBUTE_GROUPS(fpga_region);
>>  
>>  /**
>> - * fpga_region_create - alloc and init a struct fpga_region
>> + * fpga_region_register - create and register an FPGA Region device
>>   * @parent: device parent
>>   * @mgr: manager that programs this region
>> - * @get_bridges: optional function to get bridges to a list
>> + * @rops: optional: FPGA Region ops for low level FPGA Region drivers
>>   *
>> - * The caller of this function is responsible for freeing the resulting region
>> - * struct with fpga_region_free().  Using devm_fpga_region_create() instead is
>> - * recommended.
>> - *
>> - * Return: struct fpga_region or NULL
>> + * Return: struct fpga_region or ERR_PTR()
>>   */
>> -struct fpga_region
>> -*fpga_region_create(struct device *parent,
>> -		    struct fpga_manager *mgr,
>> -		    int (*get_bridges)(struct fpga_region *))
>> +struct fpga_region *
>> +fpga_region_register(struct device *parent, struct fpga_manager *mgr,
>> +		     struct fpga_region_ops *rops)
> Is it better we use the same style as fpga_mgr_register?

Can you be more specific? What style changes are you requesting?

- Russ
>
> Thanks,
> Yilun
>
>>  {
>>  	struct fpga_region *region;
>>  	int id, ret = 0;
>>  
>>  	region = kzalloc(sizeof(*region), GFP_KERNEL);
>>  	if (!region)
>> -		return NULL;
>> +		return ERR_PTR(-ENOMEM);
>>  
>>  	id = ida_simple_get(&fpga_region_ida, 0, 0, GFP_KERNEL);
>> -	if (id < 0)
>> +	if (id < 0) {
>> +		ret = id;
>>  		goto err_free;
>> +	}
>>  
>>  	region->mgr = mgr;
>> -	region->get_bridges = get_bridges;
>> +	region->rops = rops;
>>  	mutex_init(&region->mutex);
>>  	INIT_LIST_HEAD(&region->bridge_list);
>>  
>> -	device_initialize(&region->dev);
>>  	region->dev.class = fpga_region_class;
>>  	region->dev.parent = parent;
>>  	region->dev.of_node = parent->of_node;
>> @@ -222,6 +218,12 @@ struct fpga_region
>>  	if (ret)
>>  		goto err_remove;
>>  
>> +	ret = device_register(&region->dev);
>> +	if (ret) {
>> +		put_device(&region->dev);
>> +		return ERR_PTR(ret);
>> +	}
>> +
>>  	return region;
>>  
>>  err_remove:
>> @@ -229,76 +231,7 @@ struct fpga_region
>>  err_free:
>>  	kfree(region);
>>  
>> -	return NULL;
>> -}
>> -EXPORT_SYMBOL_GPL(fpga_region_create);
>> -
>> -/**
>> - * fpga_region_free - free an FPGA region created by fpga_region_create()
>> - * @region: FPGA region
>> - */
>> -void fpga_region_free(struct fpga_region *region)
>> -{
>> -	ida_simple_remove(&fpga_region_ida, region->dev.id);
>> -	kfree(region);
>> -}
>> -EXPORT_SYMBOL_GPL(fpga_region_free);
>> -
>> -static void devm_fpga_region_release(struct device *dev, void *res)
>> -{
>> -	struct fpga_region *region = *(struct fpga_region **)res;
>> -
>> -	fpga_region_free(region);
>> -}
>> -
>> -/**
>> - * devm_fpga_region_create - create and initialize a managed FPGA region struct
>> - * @parent: device parent
>> - * @mgr: manager that programs this region
>> - * @get_bridges: optional function to get bridges to a list
>> - *
>> - * This function is intended for use in an FPGA region driver's probe function.
>> - * After the region driver creates the region struct with
>> - * devm_fpga_region_create(), it should register it with fpga_region_register().
>> - * The region driver's remove function should call fpga_region_unregister().
>> - * The region 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_region_register(), the struct will still get cleaned up.
>> - *
>> - * Return: struct fpga_region or NULL
>> - */
>> -struct fpga_region
>> -*devm_fpga_region_create(struct device *parent,
>> -			 struct fpga_manager *mgr,
>> -			 int (*get_bridges)(struct fpga_region *))
>> -{
>> -	struct fpga_region **ptr, *region;
>> -
>> -	ptr = devres_alloc(devm_fpga_region_release, sizeof(*ptr), GFP_KERNEL);
>> -	if (!ptr)
>> -		return NULL;
>> -
>> -	region = fpga_region_create(parent, mgr, get_bridges);
>> -	if (!region) {
>> -		devres_free(ptr);
>> -	} else {
>> -		*ptr = region;
>> -		devres_add(parent, ptr);
>> -	}
>> -
>> -	return region;
>> -}
>> -EXPORT_SYMBOL_GPL(devm_fpga_region_create);
>> -
>> -/**
>> - * fpga_region_register - register an FPGA region
>> - * @region: FPGA region
>> - *
>> - * Return: 0 or -errno
>> - */
>> -int fpga_region_register(struct fpga_region *region)
>> -{
>> -	return device_add(&region->dev);
>> +	return ERR_PTR(ret);
>>  }
>>  EXPORT_SYMBOL_GPL(fpga_region_register);
>>  
>> @@ -316,6 +249,10 @@ EXPORT_SYMBOL_GPL(fpga_region_unregister);
>>  
>>  static void fpga_region_dev_release(struct device *dev)
>>  {
>> +	struct fpga_region *region = to_fpga_region(dev);
>> +
>> +	ida_simple_remove(&fpga_region_ida, region->dev.id);
>> +	kfree(region);
>>  }
>>  
>>  /**
>> diff --git a/drivers/fpga/of-fpga-region.c b/drivers/fpga/of-fpga-region.c
>> index e3c25576b6b9..55b7c4ec8f6d 100644
>> --- a/drivers/fpga/of-fpga-region.c
>> +++ b/drivers/fpga/of-fpga-region.c
>> @@ -392,6 +392,10 @@ static struct notifier_block fpga_region_of_nb = {
>>  	.notifier_call = of_fpga_region_notify,
>>  };
>>  
>> +static struct fpga_region_ops of_fpga_region_ops = {
>> +	.get_bridges = of_fpga_region_get_bridges;
>> +};
>> +
>>  static int of_fpga_region_probe(struct platform_device *pdev)
>>  {
>>  	struct device *dev = &pdev->dev;
>> @@ -405,16 +409,12 @@ static int of_fpga_region_probe(struct platform_device *pdev)
>>  	if (IS_ERR(mgr))
>>  		return -EPROBE_DEFER;
>>  
>> -	region = devm_fpga_region_create(dev, mgr, of_fpga_region_get_bridges);
>> -	if (!region) {
>> -		ret = -ENOMEM;
>> +	region = fpga_region_register(dev, mgr, &of_fpga_region_ops);
>> +	if (IS_ERR(region)) {
>> +		ret = PTR_ERR(region);
>>  		goto eprobe_mgr_put;
>>  	}
>>  
>> -	ret = fpga_region_register(region);
>> -	if (ret)
>> -		goto eprobe_mgr_put;
>> -
>>  	of_platform_populate(np, fpga_region_of_match, NULL, &region->dev);
>>  	platform_set_drvdata(pdev, region);
>>  
>> diff --git a/include/linux/fpga/fpga-region.h b/include/linux/fpga/fpga-region.h
>> index 27cb706275db..02626dfd41ed 100644
>> --- a/include/linux/fpga/fpga-region.h
>> +++ b/include/linux/fpga/fpga-region.h
>> @@ -7,6 +7,24 @@
>>  #include <linux/fpga/fpga-mgr.h>
>>  #include <linux/fpga/fpga-bridge.h>
>>  
>> +struct fpga_region;
>> +
>> +/**
>> + * struct fpga_region_ops - ops for low level fpga region drivers
>> + * @get_bridges: optional function to get bridges to a list
>> + * @compat_id: optional: FPGA manager id for compatibility check.
>> + * @priv: optional: private data
>> + *
>> + * fpga_region_ops are the low level functions implemented by a specific
>> + * fpga region driver.  The optional ones are tested for NULL before being
>> + * referenced, so leaving them out is fine.
>> + */
>> +struct fpga_region_ops {
>> +	int (*get_bridges)(struct fpga_region *region);
>> +	struct fpga_compat_id *compat_id;
>> +	void *priv;
>> +};
>> +
>>  /**
>>   * struct fpga_region - FPGA Region structure
>>   * @dev: FPGA Region device
>> @@ -14,9 +32,7 @@
>>   * @bridge_list: list of FPGA bridges specified in region
>>   * @mgr: FPGA manager
>>   * @info: FPGA image info
>> - * @compat_id: FPGA region id for compatibility check.
>> - * @priv: private data
>> - * @get_bridges: optional function to get bridges to a list
>> + * @rops: optional: FPGA Region ops for low level FPGA region drivers
>>   */
>>  struct fpga_region {
>>  	struct device dev;
>> @@ -24,9 +40,7 @@ struct fpga_region {
>>  	struct list_head bridge_list;
>>  	struct fpga_manager *mgr;
>>  	struct fpga_image_info *info;
>> -	struct fpga_compat_id *compat_id;
>> -	void *priv;
>> -	int (*get_bridges)(struct fpga_region *region);
>> +	struct fpga_region_ops *rops;
>>  };
>>  
>>  #define to_fpga_region(d) container_of(d, struct fpga_region, dev)
>> @@ -37,15 +51,9 @@ struct fpga_region *fpga_region_class_find(
>>  
>>  int fpga_region_program_fpga(struct fpga_region *region);
>>  
>> -struct fpga_region
>> -*fpga_region_create(struct device *dev, struct fpga_manager *mgr,
>> -		    int (*get_bridges)(struct fpga_region *));
>> -void fpga_region_free(struct fpga_region *region);
>> -int fpga_region_register(struct fpga_region *region);
>> +struct fpga_region *
>> +fpga_region_register(struct device *dev, struct fpga_manager *mgr,
>> +		     struct fpga_region_ops *rops);
>>  void fpga_region_unregister(struct fpga_region *region);
>>  
>> -struct fpga_region
>> -*devm_fpga_region_create(struct device *dev, struct fpga_manager *mgr,
>> -			int (*get_bridges)(struct fpga_region *));
>> -
>>  #endif /* _FPGA_REGION_H */
>> -- 
>> 2.25.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v5 1/3] fpga: mgr: Use standard dev_release for class driver
  2021-06-18 16:03     ` Russ Weight
@ 2021-06-18 17:58       ` Russ Weight
  2021-06-18 20:39         ` Tom Rix
                           ` (2 more replies)
  0 siblings, 3 replies; 19+ messages in thread
From: Russ Weight @ 2021-06-18 17:58 UTC (permalink / raw)
  To: Xu Yilun
  Cc: mdf, linux-fpga, trix, lgoncalv, hao.wu, matthew.gerlach, richard.gong



On 6/18/21 9:03 AM, Russ Weight wrote:
>
> On 6/18/21 8:45 AM, Xu Yilun wrote:
>> On Wed, Jun 16, 2021 at 03:57:38PM -0700, Russ Weight wrote:
>>> The FPGA manager class driver data structure is being treated as a
>>> managed resource instead of using standard dev_release call-back
>>> to release the class data structure. This change removes the
>>> managed resource code for the freeing of the class data structure
>>> and combines the create() and register() functions into a single
>>> register() function.
>>>
>>> The devm_fpga_mgr_register() function is retained.
>>>
>>> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
>>> ---
>>> v5:
>>>   - Rebased on top of recently accepted patches.
>>>   - Removed compat_id from the fpga_mgr_register() parameter list
>>>     and added it to the fpga_manager_ops structure. This also required
>> My reason for this change is, we could avoid further change of the
>> fpga_mgr_register() API if we add new input parameters later on.
> With this patchset, changes are only required for the callers
> that use the new parameters.
>
>>>     dynamically allocating the dfl-fme-ops structure in order to add
>>>     the appropriate compat_id.
>> But enforcing the dynamical allocation of the parameters is not prefered
>> to me. How about a dedicated structure that wraps all the needed
>> parameters:
>>
>> struct fpga_mgr_info {
>> 	const char *name;
>> 	const struct fpga_manager_ops *mops;
>> 	const struct fpga_compat_id *compat_id;
>> 	const void *priv;
>> };
>>
>> Then We can simply define a local variable of this struct for
>> fpga_mgr_register().
>>
>> more details inline.
> I agree the at the dynamic allocation is not preferred, but it is only
> required if compat_id is used. In all other cases, the static structure
> can continue to be used. In otherwords, only one caller is affected.
>>
>>> v4:
>>>   - Added the compat_id parameter to fpga_mgr_register() and
>>>     devm_fpga_mgr_register() to ensure that the compat_id is set before
>>>     the device_register() call.
>>> v3:
>>>   - Cleaned up comment header for fpga_mgr_register()
>>>   - Fix error return on ida_simple_get() failure
>>> v2:
>>>   - Restored devm_fpga_mgr_register() functionality, adapted for the combined
>>>     create/register functionality.
>>>   - All previous callers of devm_fpga_mgr_register() will continue to call
>>>     devm_fpga_mgr_register().
>>>   - replaced unnecessary ternary operators in return statements with standard
>>>     if conditions.
>>> ---
>>>  drivers/fpga/altera-cvp.c        |  12 +--
>>>  drivers/fpga/altera-pr-ip-core.c |   8 +-
>>>  drivers/fpga/altera-ps-spi.c     |  10 +-
>>>  drivers/fpga/dfl-fme-mgr.c       |  52 ++++++----
>>>  drivers/fpga/dfl-fme-region.c    |   2 +-
>>>  drivers/fpga/fpga-mgr.c          | 163 ++++++++-----------------------
>>>  drivers/fpga/ice40-spi.c         |  10 +-
>>>  drivers/fpga/machxo2-spi.c       |  10 +-
>>>  drivers/fpga/socfpga-a10.c       |  16 ++-
>>>  drivers/fpga/socfpga.c           |  10 +-
>>>  drivers/fpga/stratix10-soc.c     |  16 +--
>>>  drivers/fpga/ts73xx-fpga.c       |  10 +-
>>>  drivers/fpga/xilinx-spi.c        |  12 +--
>>>  drivers/fpga/zynq-fpga.c         |  16 ++-
>>>  drivers/fpga/zynqmp-fpga.c       |  10 +-
>>>  include/linux/fpga/fpga-mgr.h    |  43 ++++----
>>>  16 files changed, 153 insertions(+), 247 deletions(-)
>>>
>>> diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
>>> index ccf4546eff29..4ffb9da537d8 100644
>>> --- a/drivers/fpga/altera-cvp.c
>>> +++ b/drivers/fpga/altera-cvp.c
>>> @@ -652,19 +652,15 @@ static int altera_cvp_probe(struct pci_dev *pdev,
>>>  	snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s @%s",
>>>  		 ALTERA_CVP_MGR_NAME, pci_name(pdev));
>>>  
>>> -	mgr = devm_fpga_mgr_create(&pdev->dev, conf->mgr_name,
>>> -				   &altera_cvp_ops, conf);
>>> -	if (!mgr) {
>>> -		ret = -ENOMEM;
>>> +	mgr = fpga_mgr_register(&pdev->dev, conf->mgr_name,
>>> +				&altera_cvp_ops, conf);
>>> +	if (IS_ERR(mgr)) {
>>> +		ret = PTR_ERR(mgr);
>>>  		goto err_unmap;
>>>  	}
>>>  
>>>  	pci_set_drvdata(pdev, mgr);
>>>  
>>> -	ret = fpga_mgr_register(mgr);
>>> -	if (ret)
>>> -		goto err_unmap;
>>> -
>>>  	return 0;
>>>  
>>>  err_unmap:
>>> diff --git a/drivers/fpga/altera-pr-ip-core.c b/drivers/fpga/altera-pr-ip-core.c
>>> index dfdf21ed34c4..17babf974852 100644
>>> --- a/drivers/fpga/altera-pr-ip-core.c
>>> +++ b/drivers/fpga/altera-pr-ip-core.c
>>> @@ -191,11 +191,11 @@ int alt_pr_register(struct device *dev, void __iomem *reg_base)
>>>  		(val & ALT_PR_CSR_STATUS_MSK) >> ALT_PR_CSR_STATUS_SFT,
>>>  		(int)(val & ALT_PR_CSR_PR_START));
>>>  
>>> -	mgr = devm_fpga_mgr_create(dev, dev_name(dev), &alt_pr_ops, priv);
>>> -	if (!mgr)
>>> -		return -ENOMEM;
>>> +	mgr = devm_fpga_mgr_register(dev, dev_name(dev), &alt_pr_ops, priv);
>>> +	if (IS_ERR(mgr))
>>> +		return PTR_ERR(mgr);
>>>  
>>> -	return devm_fpga_mgr_register(dev, mgr);
>>> +	return 0;
>>>  }
>>>  EXPORT_SYMBOL_GPL(alt_pr_register);
>>>  
>>> diff --git a/drivers/fpga/altera-ps-spi.c b/drivers/fpga/altera-ps-spi.c
>>> index 23bfd4d1ad0f..d3f77b0312b2 100644
>>> --- a/drivers/fpga/altera-ps-spi.c
>>> +++ b/drivers/fpga/altera-ps-spi.c
>>> @@ -302,12 +302,12 @@ static int altera_ps_probe(struct spi_device *spi)
>>>  	snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s %s",
>>>  		 dev_driver_string(&spi->dev), dev_name(&spi->dev));
>>>  
>>> -	mgr = devm_fpga_mgr_create(&spi->dev, conf->mgr_name,
>>> -				   &altera_ps_ops, conf);
>>> -	if (!mgr)
>>> -		return -ENOMEM;
>>> +	mgr = devm_fpga_mgr_register(&spi->dev, conf->mgr_name,
>>> +				     &altera_ps_ops, conf);
>>> +	if (IS_ERR(mgr))
>>> +		return PTR_ERR(mgr);
>>>  
>>> -	return devm_fpga_mgr_register(&spi->dev, mgr);
>>> +	return 0;
>>>  }
>>>  
>>>  static const struct spi_device_id altera_ps_spi_ids[] = {
>>> diff --git a/drivers/fpga/dfl-fme-mgr.c b/drivers/fpga/dfl-fme-mgr.c
>>> index d5861d13b306..1b93bc292dbe 100644
>>> --- a/drivers/fpga/dfl-fme-mgr.c
>>> +++ b/drivers/fpga/dfl-fme-mgr.c
>>> @@ -264,14 +264,6 @@ static u64 fme_mgr_status(struct fpga_manager *mgr)
>>>  	return pr_error_to_mgr_status(priv->pr_error);
>>>  }
>>>  
>>> -static const struct fpga_manager_ops fme_mgr_ops = {
>>> -	.write_init = fme_mgr_write_init,
>>> -	.write = fme_mgr_write,
>>> -	.write_complete = fme_mgr_write_complete,
>>> -	.state = fme_mgr_state,
>>> -	.status = fme_mgr_status,
>>> -};
>>> -
>>>  static void fme_mgr_get_compat_id(void __iomem *fme_pr,
>>>  				  struct fpga_compat_id *id)
>>>  {
>>> @@ -279,10 +271,34 @@ static void fme_mgr_get_compat_id(void __iomem *fme_pr,
>>>  	id->id_h = readq(fme_pr + FME_PR_INTFC_ID_H);
>>>  }
>>>  
>>> +static struct fpga_manager_ops *fme_mgr_get_ops(struct device *dev,
>>> +						struct fme_mgr_priv *priv)
>>> +{
>>> +	struct fpga_manager_ops *ops;
>>> +
>>> +	ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL);
>>> +	if (!ops)
>>> +		return NULL;
>>> +
>>> +	ops->compat_id = devm_kzalloc(dev, sizeof(struct fpga_compat_id),
>>> +				      GFP_KERNEL);
>>> +	if (!ops->compat_id)
>>> +		return NULL;
>>> +
>>> +	fme_mgr_get_compat_id(priv->ioaddr, ops->compat_id);
>>> +	ops->write_init = fme_mgr_write_init;
>>> +	ops->write = fme_mgr_write;
>>> +	ops->write_complete = fme_mgr_write_complete;
>>> +	ops->state = fme_mgr_state;
>>> +	ops->status = fme_mgr_status;
>>> +
>>> +	return ops;
>>> +}

What do other's think? Is it better to dynamically allocate the ops structure
for users of compat_id (just one user at this time)? Or better to create an
info structure on the stack for all callers? See above for an example of a
dynamically allocated the ops structure.

To me, using the ops structure seems more standard, and the dynamic allocation,
while not optimal, does not require much more space or complexity than the static
allocation. At this time it only affects one caller.

Adding the info structure as a parameter to the register() functions adds a
little more complexity to all callers, whether or not they use the dynamic
elements of the structure.

- Russ

>>> +
>>>  static int fme_mgr_probe(struct platform_device *pdev)
>>>  {
>>>  	struct dfl_fme_mgr_pdata *pdata = dev_get_platdata(&pdev->dev);
>>> -	struct fpga_compat_id *compat_id;
>>> +	struct fpga_manager_ops *fme_mgr_ops;
>>>  	struct device *dev = &pdev->dev;
>>>  	struct fme_mgr_priv *priv;
>>>  	struct fpga_manager *mgr;
>>> @@ -302,20 +318,16 @@ static int fme_mgr_probe(struct platform_device *pdev)
>>>  			return PTR_ERR(priv->ioaddr);
>>>  	}
>>>  
>>> -	compat_id = devm_kzalloc(dev, sizeof(*compat_id), GFP_KERNEL);
>>> -	if (!compat_id)
>>> -		return -ENOMEM;
>>> -
>>> -	fme_mgr_get_compat_id(priv->ioaddr, compat_id);
>>> -
>>> -	mgr = devm_fpga_mgr_create(dev, "DFL FME FPGA Manager",
>>> -				   &fme_mgr_ops, priv);
>>> -	if (!mgr)
>>> +	fme_mgr_ops = fme_mgr_get_ops(dev, priv);
>>> +	if (!fme_mgr_ops)
>>>  		return -ENOMEM;
>>>  
>>> -	mgr->compat_id = compat_id;
>>> +	mgr = devm_fpga_mgr_register(dev, "DFL FME FPGA Manager",
>>> +				     fme_mgr_ops, priv);
>>> +	if (IS_ERR(mgr))
>>> +		return PTR_ERR(mgr);
>> With structure fpga_mgr_info, we don't need the dynamical allocation
>> anymore:
>>
>> 	struct fpga_mgr_info minfo = { 0 };
>>
>> 	minfo.name = "DFL FME FPGA Manager";
>> 	minfo.mops = &fme_mgr_ops;
>> 	minfo.compat_id = compat_id;
>> 	minfo.priv = priv;
>>
>> 	mgr = devm_fpga_mgr_register(dev, &minfo);
>>
>> Drivers could also leaves minfo.compat_id zero if they don't care.
>>
>> I also expect more opinions.
>>
>>>  
>>> -	return devm_fpga_mgr_register(dev, mgr);
>>> +	return 0;
>>>  }
>>>  
>>>  static struct platform_driver fme_mgr_driver = {
>>> diff --git a/drivers/fpga/dfl-fme-region.c b/drivers/fpga/dfl-fme-region.c
>>> index 1eeb42af1012..a64c13e198a0 100644
>>> --- a/drivers/fpga/dfl-fme-region.c
>>> +++ b/drivers/fpga/dfl-fme-region.c
>>> @@ -46,7 +46,7 @@ static int fme_region_probe(struct platform_device *pdev)
>>>  	}
>>>  
>>>  	region->priv = pdata;
>>> -	region->compat_id = mgr->compat_id;
>>> +	region->compat_id = mgr->mops->compat_id;
>> The changes should be in Patch #3, is it?
> This is the patch that moves compat_id out of mgr and into mgr->mops.
> In order to be able to compile, this patch needs the change.
>
>>>  	platform_set_drvdata(pdev, region);
>>>  
>>>  	ret = fpga_region_register(region);
>>> diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
>>> index ecb4c3c795fa..9aecfea432c8 100644
>>> --- a/drivers/fpga/fpga-mgr.c
>>> +++ b/drivers/fpga/fpga-mgr.c
>>> @@ -550,20 +550,20 @@ void fpga_mgr_unlock(struct fpga_manager *mgr)
>>>  EXPORT_SYMBOL_GPL(fpga_mgr_unlock);
>>>  
>>>  /**
>>> - * fpga_mgr_create - create and initialize an FPGA manager struct
>>> + * 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
>>>   *
>>> - * 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 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 *parent, 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)
>> fpga_mgr_register(struct device *parent, struct fpga_mgr_info *minfo)
>>
>>>  {
>>>  	struct fpga_manager *mgr;
>>>  	int id, ret;
>>> @@ -572,21 +572,23 @@ struct fpga_manager *fpga_mgr_create(struct device *parent, const char *name,
>>>  	    !mops->write_init || (!mops->write && !mops->write_sg) ||
>>>  	    (mops->write && mops->write_sg)) {
>>>  		dev_err(parent, "Attempt to register without fpga_manager_ops\n");
>>> -		return NULL;
>>> +		return ERR_PTR(-EINVAL);
>>>  	}
>>>  
>>>  	if (!name || !strlen(name)) {
>>>  		dev_err(parent, "Attempt to register with no name!\n");
>>> -		return NULL;
>>> +		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)
>>> +	if (id < 0) {
>>> +		ret = id;
>>>  		goto error_kfree;
>>> +	}
>>>  
>>>  	mutex_init(&mgr->ref_mutex);
>>>  
>>> @@ -594,7 +596,6 @@ struct fpga_manager *fpga_mgr_create(struct device *parent, const char *name,
>>>  	mgr->mops = mops;
>>>  	mgr->priv = priv;
>> 	mgr->name = minfo->name;
>> 	mgr->mops = minfo->mops;
>> 	mgr->priv = minfo->priv;
>> 	mgr->compat_id = minfo->compat_id;
>>
>>>  
>>> -	device_initialize(&mgr->dev);
>>>  	mgr->dev.class = fpga_mgr_class;
>>>  	mgr->dev.groups = mops->groups;
>>>  	mgr->dev.parent = parent;
>>> @@ -605,84 +606,6 @@ struct fpga_manager *fpga_mgr_create(struct device *parent, const char *name,
>>>  	if (ret)
>>>  		goto error_device;
>>>  
>>> -	return mgr;
>>> -
>>> -error_device:
>>> -	ida_simple_remove(&fpga_mgr_ida, id);
>>> -error_kfree:
>>> -	kfree(mgr);
>>> -
>>> -	return NULL;
>>> -}
>>> -EXPORT_SYMBOL_GPL(fpga_mgr_create);
>>> -
>>> -/**
>>> - * fpga_mgr_free - free an 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_mgr_devres *dr = res;
>>> -
>>> -	fpga_mgr_free(dr->mgr);
>>> -}
>>> -
>>> -/**
>>> - * devm_fpga_mgr_create - create and initialize a managed FPGA manager struct
>>> - * @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 an 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.
>>> - *
>>> - * Return: pointer to struct fpga_manager or NULL
>>> - */
>>> -struct fpga_manager *devm_fpga_mgr_create(struct device *parent, const char *name,
>>> -					  const struct fpga_manager_ops *mops,
>>> -					  void *priv)
>>> -{
>>> -	struct fpga_mgr_devres *dr;
>>> -
>>> -	dr = devres_alloc(devm_fpga_mgr_release, sizeof(*dr), GFP_KERNEL);
>>> -	if (!dr)
>>> -		return NULL;
>>> -
>>> -	dr->mgr = fpga_mgr_create(parent, name, mops, priv);
>>> -	if (!dr->mgr) {
>>> -		devres_free(dr);
>>> -		return NULL;
>>> -	}
>>> -
>>> -	devres_add(parent, dr);
>>> -
>>> -	return dr->mgr;
>>> -}
>>> -EXPORT_SYMBOL_GPL(devm_fpga_mgr_create);
>>> -
>>> -/**
>>> - * fpga_mgr_register - register an FPGA manager
>>> - * @mgr: fpga manager struct
>>> - *
>>> - * Return: 0 on success, negative error code otherwise.
>>> - */
>>> -int fpga_mgr_register(struct fpga_manager *mgr)
>>> -{
>>> -	int ret;
>>> -
>>>  	/*
>>>  	 * Initialize framework state by requesting low level driver read state
>>>  	 * from device.  FPGA may be in reset mode or may have been programmed
>>> @@ -690,18 +613,20 @@ int fpga_mgr_register(struct fpga_manager *mgr)
>>>  	 */
>>>  	mgr->state = mgr->mops->state(mgr);
>>>  
>>> -	ret = device_add(&mgr->dev);
>>> -	if (ret)
>>> -		goto error_device;
>>> -
>>> -	dev_info(&mgr->dev, "%s registered\n", mgr->name);
>>> +	ret = device_register(&mgr->dev);
>>> +	if (ret) {
>>> +		put_device(&mgr->dev);
>>> +		return ERR_PTR(ret);
>>> +	}
>>>  
>>> -	return 0;
>>> +	return mgr;
>>>  
>>>  error_device:
>>> -	ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
>>> +	ida_simple_remove(&fpga_mgr_ida, id);
>>> +error_kfree:
>>> +	kfree(mgr);
>>>  
>>> -	return ret;
>>> +	return ERR_PTR(ret);
>>>  }
>>>  EXPORT_SYMBOL_GPL(fpga_mgr_register);
>>>  
>>> @@ -726,14 +651,6 @@ void fpga_mgr_unregister(struct fpga_manager *mgr)
>>>  }
>>>  EXPORT_SYMBOL_GPL(fpga_mgr_unregister);
>>>  
>>> -static int fpga_mgr_devres_match(struct device *dev, void *res,
>>> -				 void *match_data)
>>> -{
>>> -	struct fpga_mgr_devres *dr = res;
>>> -
>>> -	return match_data == dr->mgr;
>>> -}
>>> -
>>>  static void devm_fpga_mgr_unregister(struct device *dev, void *res)
>>>  {
>>>  	struct fpga_mgr_devres *dr = res;
>>> @@ -743,44 +660,44 @@ static void devm_fpga_mgr_unregister(struct device *dev, void *res)
>>>  
>>>  /**
>>>   * devm_fpga_mgr_register - resource managed variant of fpga_mgr_register()
>>> - * @dev: managing device for this FPGA manager
>>> - * @mgr: 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
>>>   *
>>>   * This is the devres variant of fpga_mgr_register() for which the unregister
>>>   * function will be called automatically when the managing device is detached.
>>>   */
>>> -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr)
>>> +struct fpga_manager *
>>> +devm_fpga_mgr_register(struct device *dev, const char *name,
>>    devm_fpga_mgr_register(struct device *parent,
> Yes - thanks for catching that.
>
> - Russ
>
>>
>>> +		       const struct fpga_manager_ops *mops, void *priv)
>>>  {
>>>  	struct fpga_mgr_devres *dr;
>>> -	int ret;
>>> -
>>> -	/*
>>> -	 * Make sure that the struct fpga_manager * that is passed in is
>>> -	 * managed itself.
>>> -	 */
>>> -	if (WARN_ON(!devres_find(dev, devm_fpga_mgr_release,
>>> -				 fpga_mgr_devres_match, mgr)))
>>> -		return -EINVAL;
>>> +	struct fpga_manager *mgr;
>>>  
>>>  	dr = devres_alloc(devm_fpga_mgr_unregister, sizeof(*dr), GFP_KERNEL);
>>>  	if (!dr)
>>> -		return -ENOMEM;
>>> +		return ERR_PTR(-ENOMEM);
>>>  
>>> -	ret = fpga_mgr_register(mgr);
>>> -	if (ret) {
>>> +	mgr = fpga_mgr_register(dev, name, mops, priv);
>>> +	if (IS_ERR(mgr)) {
>>>  		devres_free(dr);
>>> -		return ret;
>>> +		return mgr;
>>>  	}
>>>  
>>>  	dr->mgr = mgr;
>>>  	devres_add(dev, dr);
>>>  
>>> -	return 0;
>>> +	return mgr;
>>>  }
>>>  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)
>>> diff --git a/drivers/fpga/ice40-spi.c b/drivers/fpga/ice40-spi.c
>>> index 69dec5af23c3..91ac25f339f3 100644
>>> --- a/drivers/fpga/ice40-spi.c
>>> +++ b/drivers/fpga/ice40-spi.c
>>> @@ -178,12 +178,12 @@ static int ice40_fpga_probe(struct spi_device *spi)
>>>  		return ret;
>>>  	}
>>>  
>>> -	mgr = devm_fpga_mgr_create(dev, "Lattice iCE40 FPGA Manager",
>>> -				   &ice40_fpga_ops, priv);
>>> -	if (!mgr)
>>> -		return -ENOMEM;
>>> +	mgr = devm_fpga_mgr_register(dev, "Lattice iCE40 FPGA Manager",
>>> +				     &ice40_fpga_ops, priv);
>>> +	if (IS_ERR(mgr))
>>> +		return PTR_ERR(mgr);
>>>  
>>> -	return devm_fpga_mgr_register(dev, mgr);
>>> +	return 0;
>>>  }
>>>  
>>>  static const struct of_device_id ice40_fpga_of_match[] = {
>>> diff --git a/drivers/fpga/machxo2-spi.c b/drivers/fpga/machxo2-spi.c
>>> index 114a64d2b7a4..0137c1e2e706 100644
>>> --- a/drivers/fpga/machxo2-spi.c
>>> +++ b/drivers/fpga/machxo2-spi.c
>>> @@ -366,12 +366,12 @@ static int machxo2_spi_probe(struct spi_device *spi)
>>>  		return -EINVAL;
>>>  	}
>>>  
>>> -	mgr = devm_fpga_mgr_create(dev, "Lattice MachXO2 SPI FPGA Manager",
>>> -				   &machxo2_ops, spi);
>>> -	if (!mgr)
>>> -		return -ENOMEM;
>>> +	mgr = devm_fpga_mgr_register(dev, "Lattice MachXO2 SPI FPGA Manager",
>>> +				     &machxo2_ops, spi);
>>> +	if (IS_ERR(mgr))
>>> +		return PTR_ERR(mgr);
>>>  
>>> -	return devm_fpga_mgr_register(dev, mgr);
>>> +	return 0;
>>>  }
>>>  
>>>  static const struct of_device_id of_match[] = {
>>> diff --git a/drivers/fpga/socfpga-a10.c b/drivers/fpga/socfpga-a10.c
>>> index 573d88bdf730..ac8e89b8a5cc 100644
>>> --- a/drivers/fpga/socfpga-a10.c
>>> +++ b/drivers/fpga/socfpga-a10.c
>>> @@ -508,19 +508,15 @@ static int socfpga_a10_fpga_probe(struct platform_device *pdev)
>>>  		return -EBUSY;
>>>  	}
>>>  
>>> -	mgr = devm_fpga_mgr_create(dev, "SoCFPGA Arria10 FPGA Manager",
>>> -				   &socfpga_a10_fpga_mgr_ops, priv);
>>> -	if (!mgr)
>>> -		return -ENOMEM;
>>> -
>>> -	platform_set_drvdata(pdev, mgr);
>>> -
>>> -	ret = fpga_mgr_register(mgr);
>>> -	if (ret) {
>>> +	mgr = fpga_mgr_register(dev, "SoCFPGA Arria10 FPGA Manager",
>>> +				&socfpga_a10_fpga_mgr_ops, priv);
>>> +	if (IS_ERR(mgr)) {
>>>  		clk_disable_unprepare(priv->clk);
>>> -		return ret;
>>> +		return PTR_ERR(mgr);
>>>  	}
>>>  
>>> +	platform_set_drvdata(pdev, mgr);
>>> +
>>>  	return 0;
>>>  }
>>>  
>>> diff --git a/drivers/fpga/socfpga.c b/drivers/fpga/socfpga.c
>>> index 1f467173fc1f..ebfa9f4d24ef 100644
>>> --- a/drivers/fpga/socfpga.c
>>> +++ b/drivers/fpga/socfpga.c
>>> @@ -571,12 +571,12 @@ static int socfpga_fpga_probe(struct platform_device *pdev)
>>>  	if (ret)
>>>  		return ret;
>>>  
>>> -	mgr = devm_fpga_mgr_create(dev, "Altera SOCFPGA FPGA Manager",
>>> -				   &socfpga_fpga_ops, priv);
>>> -	if (!mgr)
>>> -		return -ENOMEM;
>>> +	mgr = devm_fpga_mgr_register(dev, "Altera SOCFPGA FPGA Manager",
>>> +				     &socfpga_fpga_ops, priv);
>>> +	if (IS_ERR(mgr))
>>> +		return PTR_ERR(mgr);
>>>  
>>> -	return devm_fpga_mgr_register(dev, mgr);
>>> +	return 0;
>>>  }
>>>  
>>>  #ifdef CONFIG_OF
>>> diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c
>>> index a2cea500f7cc..9155e888a133 100644
>>> --- a/drivers/fpga/stratix10-soc.c
>>> +++ b/drivers/fpga/stratix10-soc.c
>>> @@ -425,18 +425,11 @@ static int s10_probe(struct platform_device *pdev)
>>>  
>>>  	init_completion(&priv->status_return_completion);
>>>  
>>> -	mgr = fpga_mgr_create(dev, "Stratix10 SOC FPGA Manager",
>>> -			      &s10_ops, priv);
>>> -	if (!mgr) {
>>> -		dev_err(dev, "unable to create FPGA manager\n");
>>> -		ret = -ENOMEM;
>>> -		goto probe_err;
>>> -	}
>>> -
>>> -	ret = fpga_mgr_register(mgr);
>>> -	if (ret) {
>>> +	mgr = fpga_mgr_register(dev, "Stratix10 SOC FPGA Manager",
>>> +				&s10_ops, priv);
>>> +	if (IS_ERR(mgr)) {
>>>  		dev_err(dev, "unable to register FPGA manager\n");
>>> -		fpga_mgr_free(mgr);
>>> +		ret = PTR_ERR(mgr);
>>>  		goto probe_err;
>>>  	}
>>>  
>>> @@ -454,7 +447,6 @@ static int s10_remove(struct platform_device *pdev)
>>>  	struct s10_priv *priv = mgr->priv;
>>>  
>>>  	fpga_mgr_unregister(mgr);
>>> -	fpga_mgr_free(mgr);
>>>  	stratix10_svc_free_channel(priv->chan);
>>>  
>>>  	return 0;
>>> diff --git a/drivers/fpga/ts73xx-fpga.c b/drivers/fpga/ts73xx-fpga.c
>>> index 101f016c6ed8..5efbec2b9f9f 100644
>>> --- a/drivers/fpga/ts73xx-fpga.c
>>> +++ b/drivers/fpga/ts73xx-fpga.c
>>> @@ -122,12 +122,12 @@ static int ts73xx_fpga_probe(struct platform_device *pdev)
>>>  	if (IS_ERR(priv->io_base))
>>>  		return PTR_ERR(priv->io_base);
>>>  
>>> -	mgr = devm_fpga_mgr_create(kdev, "TS-73xx FPGA Manager",
>>> -				   &ts73xx_fpga_ops, priv);
>>> -	if (!mgr)
>>> -		return -ENOMEM;
>>> +	mgr = devm_fpga_mgr_register(kdev, "TS-73xx FPGA Manager",
>>> +				     &ts73xx_fpga_ops, priv);
>>> +	if (IS_ERR(mgr))
>>> +		return PTR_ERR(mgr);
>>>  
>>> -	return devm_fpga_mgr_register(kdev, mgr);
>>> +	return 0;
>>>  }
>>>  
>>>  static struct platform_driver ts73xx_fpga_driver = {
>>> diff --git a/drivers/fpga/xilinx-spi.c b/drivers/fpga/xilinx-spi.c
>>> index fee4d0abf6bf..efda475f7a98 100644
>>> --- a/drivers/fpga/xilinx-spi.c
>>> +++ b/drivers/fpga/xilinx-spi.c
>>> @@ -247,13 +247,13 @@ static int xilinx_spi_probe(struct spi_device *spi)
>>>  		return dev_err_probe(&spi->dev, PTR_ERR(conf->done),
>>>  				     "Failed to get DONE gpio\n");
>>>  
>>> -	mgr = devm_fpga_mgr_create(&spi->dev,
>>> -				   "Xilinx Slave Serial FPGA Manager",
>>> -				   &xilinx_spi_ops, conf);
>>> -	if (!mgr)
>>> -		return -ENOMEM;
>>> +	mgr = devm_fpga_mgr_register(&spi->dev,
>>> +				     "Xilinx Slave Serial FPGA Manager",
>>> +				     &xilinx_spi_ops, conf);
>>> +	if (IS_ERR(mgr))
>>> +		return PTR_ERR(mgr);
>>>  
>>> -	return devm_fpga_mgr_register(&spi->dev, mgr);
>>> +	return 0;
>>>  }
>>>  
>>>  static const struct of_device_id xlnx_spi_of_match[] = {
>>> diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c
>>> index 9b75bd4f93d8..426aa34c6a0d 100644
>>> --- a/drivers/fpga/zynq-fpga.c
>>> +++ b/drivers/fpga/zynq-fpga.c
>>> @@ -609,20 +609,16 @@ static int zynq_fpga_probe(struct platform_device *pdev)
>>>  
>>>  	clk_disable(priv->clk);
>>>  
>>> -	mgr = devm_fpga_mgr_create(dev, "Xilinx Zynq FPGA Manager",
>>> -				   &zynq_fpga_ops, priv);
>>> -	if (!mgr)
>>> -		return -ENOMEM;
>>> -
>>> -	platform_set_drvdata(pdev, mgr);
>>> -
>>> -	err = fpga_mgr_register(mgr);
>>> -	if (err) {
>>> +	mgr = fpga_mgr_register(dev, "Xilinx Zynq FPGA Manager",
>>> +				&zynq_fpga_ops, priv);
>>> +	if (IS_ERR(mgr)) {
>>>  		dev_err(dev, "unable to register FPGA manager\n");
>>>  		clk_unprepare(priv->clk);
>>> -		return err;
>>> +		return PTR_ERR(mgr);
>>>  	}
>>>  
>>> +	platform_set_drvdata(pdev, mgr);
>>> +
>>>  	return 0;
>>>  }
>>>  
>>> diff --git a/drivers/fpga/zynqmp-fpga.c b/drivers/fpga/zynqmp-fpga.c
>>> index 125743c9797f..1a6d520985be 100644
>>> --- a/drivers/fpga/zynqmp-fpga.c
>>> +++ b/drivers/fpga/zynqmp-fpga.c
>>> @@ -102,12 +102,12 @@ static int zynqmp_fpga_probe(struct platform_device *pdev)
>>>  
>>>  	priv->dev = dev;
>>>  
>>> -	mgr = devm_fpga_mgr_create(dev, "Xilinx ZynqMP FPGA Manager",
>>> -				   &zynqmp_fpga_ops, priv);
>>> -	if (!mgr)
>>> -		return -ENOMEM;
>>> +	mgr = devm_fpga_mgr_register(dev, "Xilinx ZynqMP FPGA Manager",
>>> +				     &zynqmp_fpga_ops, priv);
>>> +	if (IS_ERR(mgr))
>>> +		return PTR_ERR(mgr);
>>>  
>>> -	return devm_fpga_mgr_register(dev, mgr);
>>> +	return 0;
>>>  }
>>>  
>>>  static const struct of_device_id zynqmp_fpga_of_match[] = {
>>> diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
>>> index 474c1f506307..d3c6f0153457 100644
>>> --- a/include/linux/fpga/fpga-mgr.h
>>> +++ b/include/linux/fpga/fpga-mgr.h
>>> @@ -105,6 +105,17 @@ struct fpga_image_info {
>>>  #endif
>>>  };
>>>  
>>> +/**
>>> + * struct fpga_compat_id - id for compatibility check
>>> + *
>>> + * @id_h: high 64bit of the compat_id
>>> + * @id_l: low 64bit of the compat_id
>>> + */
>>> +struct fpga_compat_id {
>>> +	u64 id_h;
>>> +	u64 id_l;
>>> +};
>>> +
>>>  /**
>>>   * struct fpga_manager_ops - ops for low level fpga manager drivers
>>>   * @initial_header_size: Maximum number of bytes that should be passed into write_init
>>> @@ -116,6 +127,7 @@ struct fpga_image_info {
>>>   * @write_complete: set FPGA to operating state after writing is done
>>>   * @fpga_remove: optional: Set FPGA into a specific state during driver remove
>>>   * @groups: optional attribute groups.
>>> + * @compat_id: optional: FPGA manager id for compatibility check.
>>>   *
>>>   * fpga_manager_ops are the low level functions implemented by a specific
>>>   * fpga manager driver.  The optional ones are tested for NULL before being
>>> @@ -134,6 +146,7 @@ struct fpga_manager_ops {
>>>  			      struct fpga_image_info *info);
>>>  	void (*fpga_remove)(struct fpga_manager *mgr);
>>>  	const struct attribute_group **groups;
>>> +	struct fpga_compat_id *compat_id;
>> We don't have to move compat_id here, keep it in struct fpga_mgr
>>
>> Thanks,
>> Yilun
>>
>>>  };
>>>  
>>>  /* FPGA manager status: Partial/Full Reconfiguration errors */
>>> @@ -143,24 +156,12 @@ struct fpga_manager_ops {
>>>  #define FPGA_MGR_STATUS_IP_PROTOCOL_ERR		BIT(3)
>>>  #define FPGA_MGR_STATUS_FIFO_OVERFLOW_ERR	BIT(4)
>>>  
>>> -/**
>>> - * struct fpga_compat_id - id for compatibility check
>>> - *
>>> - * @id_h: high 64bit of the compat_id
>>> - * @id_l: low 64bit of the compat_id
>>> - */
>>> -struct fpga_compat_id {
>>> -	u64 id_h;
>>> -	u64 id_l;
>>> -};
>>> -
>>>  /**
>>>   * struct fpga_manager - fpga manager structure
>>>   * @name: name of low level fpga manager
>>>   * @dev: fpga manager device
>>>   * @ref_mutex: only allows one reference to fpga manager
>>>   * @state: state of fpga manager
>>> - * @compat_id: FPGA manager id for compatibility check.
>>>   * @mops: pointer to struct of fpga manager ops
>>>   * @priv: low level driver private date
>>>   */
>>> @@ -169,7 +170,6 @@ struct fpga_manager {
>>>  	struct device dev;
>>>  	struct mutex ref_mutex;
>>>  	enum fpga_mgr_states state;
>>> -	struct fpga_compat_id *compat_id;
>>>  	const struct fpga_manager_ops *mops;
>>>  	void *priv;
>>>  };
>>> @@ -191,17 +191,14 @@ struct fpga_manager *fpga_mgr_get(struct device *dev);
>>>  
>>>  void fpga_mgr_put(struct fpga_manager *mgr);
>>>  
>>> -struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
>>> -				     const struct fpga_manager_ops *mops,
>>> -				     void *priv);
>>> -void fpga_mgr_free(struct fpga_manager *mgr);
>>> -int fpga_mgr_register(struct fpga_manager *mgr);
>>> +struct fpga_manager *fpga_mgr_register(struct device *dev, const char *name,
>>> +				       const struct fpga_manager_ops *mops,
>>> +				       void *priv);
>>>  void fpga_mgr_unregister(struct fpga_manager *mgr);
>>>  
>>> -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr);
>>> -
>>> -struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name,
>>> -					  const struct fpga_manager_ops *mops,
>>> -					  void *priv);
>>> +struct fpga_manager *
>>> +devm_fpga_mgr_register(struct device *dev, const char *name,
>>> +		       const struct fpga_manager_ops *mops,
>>> +		       void *priv);
>>>  
>>>  #endif /*_LINUX_FPGA_MGR_H */
>>> -- 
>>> 2.25.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v5 1/3] fpga: mgr: Use standard dev_release for class driver
  2021-06-18 17:58       ` Russ Weight
@ 2021-06-18 20:39         ` Tom Rix
  2021-06-18 22:01           ` Moritz Fischer
  2021-06-18 22:31           ` Russ Weight
  2021-06-21  6:47         ` Wu, Hao
  2021-06-21  7:36         ` Xu Yilun
  2 siblings, 2 replies; 19+ messages in thread
From: Tom Rix @ 2021-06-18 20:39 UTC (permalink / raw)
  To: Russ Weight, Xu Yilun
  Cc: mdf, linux-fpga, lgoncalv, hao.wu, matthew.gerlach, richard.gong


On 6/18/21 10:58 AM, Russ Weight wrote:
>
> On 6/18/21 9:03 AM, Russ Weight wrote:
>> On 6/18/21 8:45 AM, Xu Yilun wrote:
>>> On Wed, Jun 16, 2021 at 03:57:38PM -0700, Russ Weight wrote:
>>>> The FPGA manager class driver data structure is being treated as a
>>>> managed resource instead of using standard dev_release call-back
>>>> to release the class data structure. This change removes the
>>>> managed resource code for the freeing of the class data structure
>>>> and combines the create() and register() functions into a single
>>>> register() function.
>>>>
>>>> The devm_fpga_mgr_register() function is retained.
>>>>
>>>> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
>>>> ---
>>>> v5:
>>>>    - Rebased on top of recently accepted patches.
>>>>    - Removed compat_id from the fpga_mgr_register() parameter list
>>>>      and added it to the fpga_manager_ops structure. This also required
>>> My reason for this change is, we could avoid further change of the
>>> fpga_mgr_register() API if we add new input parameters later on.
>> With this patchset, changes are only required for the callers
>> that use the new parameters.
>>
>>>>      dynamically allocating the dfl-fme-ops structure in order to add
>>>>      the appropriate compat_id.
>>> But enforcing the dynamical allocation of the parameters is not prefered
>>> to me. How about a dedicated structure that wraps all the needed
>>> parameters:
>>>
>>> struct fpga_mgr_info {
>>> 	const char *name;
>>> 	const struct fpga_manager_ops *mops;
>>> 	const struct fpga_compat_id *compat_id;
>>> 	const void *priv;
>>> };
>>>
>>> Then We can simply define a local variable of this struct for
>>> fpga_mgr_register().
>>>
>>> more details inline.
>> I agree the at the dynamic allocation is not preferred, but it is only
>> required if compat_id is used. In all other cases, the static structure
>> can continue to be used. In otherwords, only one caller is affected.
>>>> v4:
>>>>    - Added the compat_id parameter to fpga_mgr_register() and
>>>>      devm_fpga_mgr_register() to ensure that the compat_id is set before
>>>>      the device_register() call.
>>>> v3:
>>>>    - Cleaned up comment header for fpga_mgr_register()
>>>>    - Fix error return on ida_simple_get() failure
>>>> v2:
>>>>    - Restored devm_fpga_mgr_register() functionality, adapted for the combined
>>>>      create/register functionality.
>>>>    - All previous callers of devm_fpga_mgr_register() will continue to call
>>>>      devm_fpga_mgr_register().
>>>>    - replaced unnecessary ternary operators in return statements with standard
>>>>      if conditions.
>>>> ---
>>>>   drivers/fpga/altera-cvp.c        |  12 +--
>>>>   drivers/fpga/altera-pr-ip-core.c |   8 +-
>>>>   drivers/fpga/altera-ps-spi.c     |  10 +-
>>>>   drivers/fpga/dfl-fme-mgr.c       |  52 ++++++----
>>>>   drivers/fpga/dfl-fme-region.c    |   2 +-
>>>>   drivers/fpga/fpga-mgr.c          | 163 ++++++++-----------------------
>>>>   drivers/fpga/ice40-spi.c         |  10 +-
>>>>   drivers/fpga/machxo2-spi.c       |  10 +-
>>>>   drivers/fpga/socfpga-a10.c       |  16 ++-
>>>>   drivers/fpga/socfpga.c           |  10 +-
>>>>   drivers/fpga/stratix10-soc.c     |  16 +--
>>>>   drivers/fpga/ts73xx-fpga.c       |  10 +-
>>>>   drivers/fpga/xilinx-spi.c        |  12 +--
>>>>   drivers/fpga/zynq-fpga.c         |  16 ++-
>>>>   drivers/fpga/zynqmp-fpga.c       |  10 +-
>>>>   include/linux/fpga/fpga-mgr.h    |  43 ++++----
>>>>   16 files changed, 153 insertions(+), 247 deletions(-)
>>>>
>>>> diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
>>>> index ccf4546eff29..4ffb9da537d8 100644
>>>> --- a/drivers/fpga/altera-cvp.c
>>>> +++ b/drivers/fpga/altera-cvp.c
>>>> @@ -652,19 +652,15 @@ static int altera_cvp_probe(struct pci_dev *pdev,
>>>>   	snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s @%s",
>>>>   		 ALTERA_CVP_MGR_NAME, pci_name(pdev));
>>>>   
>>>> -	mgr = devm_fpga_mgr_create(&pdev->dev, conf->mgr_name,
>>>> -				   &altera_cvp_ops, conf);
>>>> -	if (!mgr) {
>>>> -		ret = -ENOMEM;
>>>> +	mgr = fpga_mgr_register(&pdev->dev, conf->mgr_name,
>>>> +				&altera_cvp_ops, conf);
>>>> +	if (IS_ERR(mgr)) {
>>>> +		ret = PTR_ERR(mgr);
>>>>   		goto err_unmap;
>>>>   	}
>>>>   
>>>>   	pci_set_drvdata(pdev, mgr);
>>>>   
>>>> -	ret = fpga_mgr_register(mgr);
>>>> -	if (ret)
>>>> -		goto err_unmap;
>>>> -
>>>>   	return 0;
>>>>   
>>>>   err_unmap:
>>>> diff --git a/drivers/fpga/altera-pr-ip-core.c b/drivers/fpga/altera-pr-ip-core.c
>>>> index dfdf21ed34c4..17babf974852 100644
>>>> --- a/drivers/fpga/altera-pr-ip-core.c
>>>> +++ b/drivers/fpga/altera-pr-ip-core.c
>>>> @@ -191,11 +191,11 @@ int alt_pr_register(struct device *dev, void __iomem *reg_base)
>>>>   		(val & ALT_PR_CSR_STATUS_MSK) >> ALT_PR_CSR_STATUS_SFT,
>>>>   		(int)(val & ALT_PR_CSR_PR_START));
>>>>   
>>>> -	mgr = devm_fpga_mgr_create(dev, dev_name(dev), &alt_pr_ops, priv);
>>>> -	if (!mgr)
>>>> -		return -ENOMEM;
>>>> +	mgr = devm_fpga_mgr_register(dev, dev_name(dev), &alt_pr_ops, priv);
>>>> +	if (IS_ERR(mgr))
>>>> +		return PTR_ERR(mgr);
>>>>   
>>>> -	return devm_fpga_mgr_register(dev, mgr);
>>>> +	return 0;
>>>>   }
>>>>   EXPORT_SYMBOL_GPL(alt_pr_register);
>>>>   
>>>> diff --git a/drivers/fpga/altera-ps-spi.c b/drivers/fpga/altera-ps-spi.c
>>>> index 23bfd4d1ad0f..d3f77b0312b2 100644
>>>> --- a/drivers/fpga/altera-ps-spi.c
>>>> +++ b/drivers/fpga/altera-ps-spi.c
>>>> @@ -302,12 +302,12 @@ static int altera_ps_probe(struct spi_device *spi)
>>>>   	snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s %s",
>>>>   		 dev_driver_string(&spi->dev), dev_name(&spi->dev));
>>>>   
>>>> -	mgr = devm_fpga_mgr_create(&spi->dev, conf->mgr_name,
>>>> -				   &altera_ps_ops, conf);
>>>> -	if (!mgr)
>>>> -		return -ENOMEM;
>>>> +	mgr = devm_fpga_mgr_register(&spi->dev, conf->mgr_name,
>>>> +				     &altera_ps_ops, conf);
>>>> +	if (IS_ERR(mgr))
>>>> +		return PTR_ERR(mgr);
>>>>   
>>>> -	return devm_fpga_mgr_register(&spi->dev, mgr);
>>>> +	return 0;
>>>>   }
>>>>   
>>>>   static const struct spi_device_id altera_ps_spi_ids[] = {
>>>> diff --git a/drivers/fpga/dfl-fme-mgr.c b/drivers/fpga/dfl-fme-mgr.c
>>>> index d5861d13b306..1b93bc292dbe 100644
>>>> --- a/drivers/fpga/dfl-fme-mgr.c
>>>> +++ b/drivers/fpga/dfl-fme-mgr.c
>>>> @@ -264,14 +264,6 @@ static u64 fme_mgr_status(struct fpga_manager *mgr)
>>>>   	return pr_error_to_mgr_status(priv->pr_error);
>>>>   }
>>>>   
>>>> -static const struct fpga_manager_ops fme_mgr_ops = {
>>>> -	.write_init = fme_mgr_write_init,
>>>> -	.write = fme_mgr_write,
>>>> -	.write_complete = fme_mgr_write_complete,
>>>> -	.state = fme_mgr_state,
>>>> -	.status = fme_mgr_status,
>>>> -};
>>>> -
>>>>   static void fme_mgr_get_compat_id(void __iomem *fme_pr,
>>>>   				  struct fpga_compat_id *id)
>>>>   {
>>>> @@ -279,10 +271,34 @@ static void fme_mgr_get_compat_id(void __iomem *fme_pr,
>>>>   	id->id_h = readq(fme_pr + FME_PR_INTFC_ID_H);
>>>>   }
>>>>   
>>>> +static struct fpga_manager_ops *fme_mgr_get_ops(struct device *dev,
>>>> +						struct fme_mgr_priv *priv)
>>>> +{
>>>> +	struct fpga_manager_ops *ops;
>>>> +
>>>> +	ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL);
>>>> +	if (!ops)
>>>> +		return NULL;
>>>> +
>>>> +	ops->compat_id = devm_kzalloc(dev, sizeof(struct fpga_compat_id),
>>>> +				      GFP_KERNEL);
>>>> +	if (!ops->compat_id)
>>>> +		return NULL;
>>>> +
>>>> +	fme_mgr_get_compat_id(priv->ioaddr, ops->compat_id);
>>>> +	ops->write_init = fme_mgr_write_init;
>>>> +	ops->write = fme_mgr_write;
>>>> +	ops->write_complete = fme_mgr_write_complete;
>>>> +	ops->state = fme_mgr_state;
>>>> +	ops->status = fme_mgr_status;
>>>> +
>>>> +	return ops;
>>>> +}
> What do other's think? Is it better to dynamically allocate the ops structure
> for users of compat_id (just one user at this time)? Or better to create an
> info structure on the stack for all callers? See above for an example of a
> dynamically allocated the ops structure.
>
> To me, using the ops structure seems more standard, and the dynamic allocation,
> while not optimal, does not require much more space or complexity than the static
> allocation. At this time it only affects one caller.
>
> Adding the info structure as a parameter to the register() functions adds a
> little more complexity to all callers, whether or not they use the dynamic
> elements of the structure.

Looks like dfl is the only user of compat_id.

A board specific value does not belong in a common structure, it belongs 
in a board structure

Move compat_id out of fpga-mgr.h and into dfl.h

In dfl- you can do whatever you want.

And leave the others' ops as-is.


Tom

>
> - Russ
>
>>>> +
>>>>   static int fme_mgr_probe(struct platform_device *pdev)
>>>>   {
>>>>   	struct dfl_fme_mgr_pdata *pdata = dev_get_platdata(&pdev->dev);
>>>> -	struct fpga_compat_id *compat_id;
>>>> +	struct fpga_manager_ops *fme_mgr_ops;
>>>>   	struct device *dev = &pdev->dev;
>>>>   	struct fme_mgr_priv *priv;
>>>>   	struct fpga_manager *mgr;
>>>> @@ -302,20 +318,16 @@ static int fme_mgr_probe(struct platform_device *pdev)
>>>>   			return PTR_ERR(priv->ioaddr);
>>>>   	}
>>>>   
>>>> -	compat_id = devm_kzalloc(dev, sizeof(*compat_id), GFP_KERNEL);
>>>> -	if (!compat_id)
>>>> -		return -ENOMEM;
>>>> -
>>>> -	fme_mgr_get_compat_id(priv->ioaddr, compat_id);
>>>> -
>>>> -	mgr = devm_fpga_mgr_create(dev, "DFL FME FPGA Manager",
>>>> -				   &fme_mgr_ops, priv);
>>>> -	if (!mgr)
>>>> +	fme_mgr_ops = fme_mgr_get_ops(dev, priv);
>>>> +	if (!fme_mgr_ops)
>>>>   		return -ENOMEM;
>>>>   
>>>> -	mgr->compat_id = compat_id;
>>>> +	mgr = devm_fpga_mgr_register(dev, "DFL FME FPGA Manager",
>>>> +				     fme_mgr_ops, priv);
>>>> +	if (IS_ERR(mgr))
>>>> +		return PTR_ERR(mgr);
>>> With structure fpga_mgr_info, we don't need the dynamical allocation
>>> anymore:
>>>
>>> 	struct fpga_mgr_info minfo = { 0 };
>>>
>>> 	minfo.name = "DFL FME FPGA Manager";
>>> 	minfo.mops = &fme_mgr_ops;
>>> 	minfo.compat_id = compat_id;
>>> 	minfo.priv = priv;
>>>
>>> 	mgr = devm_fpga_mgr_register(dev, &minfo);
>>>
>>> Drivers could also leaves minfo.compat_id zero if they don't care.
>>>
>>> I also expect more opinions.
>>>
>>>>   
>>>> -	return devm_fpga_mgr_register(dev, mgr);
>>>> +	return 0;
>>>>   }
>>>>   
>>>>   static struct platform_driver fme_mgr_driver = {
>>>> diff --git a/drivers/fpga/dfl-fme-region.c b/drivers/fpga/dfl-fme-region.c
>>>> index 1eeb42af1012..a64c13e198a0 100644
>>>> --- a/drivers/fpga/dfl-fme-region.c
>>>> +++ b/drivers/fpga/dfl-fme-region.c
>>>> @@ -46,7 +46,7 @@ static int fme_region_probe(struct platform_device *pdev)
>>>>   	}
>>>>   
>>>>   	region->priv = pdata;
>>>> -	region->compat_id = mgr->compat_id;
>>>> +	region->compat_id = mgr->mops->compat_id;
>>> The changes should be in Patch #3, is it?
>> This is the patch that moves compat_id out of mgr and into mgr->mops.
>> In order to be able to compile, this patch needs the change.
>>
>>>>   	platform_set_drvdata(pdev, region);
>>>>   
>>>>   	ret = fpga_region_register(region);
>>>> diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
>>>> index ecb4c3c795fa..9aecfea432c8 100644
>>>> --- a/drivers/fpga/fpga-mgr.c
>>>> +++ b/drivers/fpga/fpga-mgr.c
>>>> @@ -550,20 +550,20 @@ void fpga_mgr_unlock(struct fpga_manager *mgr)
>>>>   EXPORT_SYMBOL_GPL(fpga_mgr_unlock);
>>>>   
>>>>   /**
>>>> - * fpga_mgr_create - create and initialize an FPGA manager struct
>>>> + * 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
>>>>    *
>>>> - * 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 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 *parent, 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)
>>> fpga_mgr_register(struct device *parent, struct fpga_mgr_info *minfo)
>>>
>>>>   {
>>>>   	struct fpga_manager *mgr;
>>>>   	int id, ret;
>>>> @@ -572,21 +572,23 @@ struct fpga_manager *fpga_mgr_create(struct device *parent, const char *name,
>>>>   	    !mops->write_init || (!mops->write && !mops->write_sg) ||
>>>>   	    (mops->write && mops->write_sg)) {
>>>>   		dev_err(parent, "Attempt to register without fpga_manager_ops\n");
>>>> -		return NULL;
>>>> +		return ERR_PTR(-EINVAL);
>>>>   	}
>>>>   
>>>>   	if (!name || !strlen(name)) {
>>>>   		dev_err(parent, "Attempt to register with no name!\n");
>>>> -		return NULL;
>>>> +		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)
>>>> +	if (id < 0) {
>>>> +		ret = id;
>>>>   		goto error_kfree;
>>>> +	}
>>>>   
>>>>   	mutex_init(&mgr->ref_mutex);
>>>>   
>>>> @@ -594,7 +596,6 @@ struct fpga_manager *fpga_mgr_create(struct device *parent, const char *name,
>>>>   	mgr->mops = mops;
>>>>   	mgr->priv = priv;
>>> 	mgr->name = minfo->name;
>>> 	mgr->mops = minfo->mops;
>>> 	mgr->priv = minfo->priv;
>>> 	mgr->compat_id = minfo->compat_id;
>>>
>>>>   
>>>> -	device_initialize(&mgr->dev);
>>>>   	mgr->dev.class = fpga_mgr_class;
>>>>   	mgr->dev.groups = mops->groups;
>>>>   	mgr->dev.parent = parent;
>>>> @@ -605,84 +606,6 @@ struct fpga_manager *fpga_mgr_create(struct device *parent, const char *name,
>>>>   	if (ret)
>>>>   		goto error_device;
>>>>   
>>>> -	return mgr;
>>>> -
>>>> -error_device:
>>>> -	ida_simple_remove(&fpga_mgr_ida, id);
>>>> -error_kfree:
>>>> -	kfree(mgr);
>>>> -
>>>> -	return NULL;
>>>> -}
>>>> -EXPORT_SYMBOL_GPL(fpga_mgr_create);
>>>> -
>>>> -/**
>>>> - * fpga_mgr_free - free an 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_mgr_devres *dr = res;
>>>> -
>>>> -	fpga_mgr_free(dr->mgr);
>>>> -}
>>>> -
>>>> -/**
>>>> - * devm_fpga_mgr_create - create and initialize a managed FPGA manager struct
>>>> - * @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 an 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.
>>>> - *
>>>> - * Return: pointer to struct fpga_manager or NULL
>>>> - */
>>>> -struct fpga_manager *devm_fpga_mgr_create(struct device *parent, const char *name,
>>>> -					  const struct fpga_manager_ops *mops,
>>>> -					  void *priv)
>>>> -{
>>>> -	struct fpga_mgr_devres *dr;
>>>> -
>>>> -	dr = devres_alloc(devm_fpga_mgr_release, sizeof(*dr), GFP_KERNEL);
>>>> -	if (!dr)
>>>> -		return NULL;
>>>> -
>>>> -	dr->mgr = fpga_mgr_create(parent, name, mops, priv);
>>>> -	if (!dr->mgr) {
>>>> -		devres_free(dr);
>>>> -		return NULL;
>>>> -	}
>>>> -
>>>> -	devres_add(parent, dr);
>>>> -
>>>> -	return dr->mgr;
>>>> -}
>>>> -EXPORT_SYMBOL_GPL(devm_fpga_mgr_create);
>>>> -
>>>> -/**
>>>> - * fpga_mgr_register - register an FPGA manager
>>>> - * @mgr: fpga manager struct
>>>> - *
>>>> - * Return: 0 on success, negative error code otherwise.
>>>> - */
>>>> -int fpga_mgr_register(struct fpga_manager *mgr)
>>>> -{
>>>> -	int ret;
>>>> -
>>>>   	/*
>>>>   	 * Initialize framework state by requesting low level driver read state
>>>>   	 * from device.  FPGA may be in reset mode or may have been programmed
>>>> @@ -690,18 +613,20 @@ int fpga_mgr_register(struct fpga_manager *mgr)
>>>>   	 */
>>>>   	mgr->state = mgr->mops->state(mgr);
>>>>   
>>>> -	ret = device_add(&mgr->dev);
>>>> -	if (ret)
>>>> -		goto error_device;
>>>> -
>>>> -	dev_info(&mgr->dev, "%s registered\n", mgr->name);
>>>> +	ret = device_register(&mgr->dev);
>>>> +	if (ret) {
>>>> +		put_device(&mgr->dev);
>>>> +		return ERR_PTR(ret);
>>>> +	}
>>>>   
>>>> -	return 0;
>>>> +	return mgr;
>>>>   
>>>>   error_device:
>>>> -	ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
>>>> +	ida_simple_remove(&fpga_mgr_ida, id);
>>>> +error_kfree:
>>>> +	kfree(mgr);
>>>>   
>>>> -	return ret;
>>>> +	return ERR_PTR(ret);
>>>>   }
>>>>   EXPORT_SYMBOL_GPL(fpga_mgr_register);
>>>>   
>>>> @@ -726,14 +651,6 @@ void fpga_mgr_unregister(struct fpga_manager *mgr)
>>>>   }
>>>>   EXPORT_SYMBOL_GPL(fpga_mgr_unregister);
>>>>   
>>>> -static int fpga_mgr_devres_match(struct device *dev, void *res,
>>>> -				 void *match_data)
>>>> -{
>>>> -	struct fpga_mgr_devres *dr = res;
>>>> -
>>>> -	return match_data == dr->mgr;
>>>> -}
>>>> -
>>>>   static void devm_fpga_mgr_unregister(struct device *dev, void *res)
>>>>   {
>>>>   	struct fpga_mgr_devres *dr = res;
>>>> @@ -743,44 +660,44 @@ static void devm_fpga_mgr_unregister(struct device *dev, void *res)
>>>>   
>>>>   /**
>>>>    * devm_fpga_mgr_register - resource managed variant of fpga_mgr_register()
>>>> - * @dev: managing device for this FPGA manager
>>>> - * @mgr: 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
>>>>    *
>>>>    * This is the devres variant of fpga_mgr_register() for which the unregister
>>>>    * function will be called automatically when the managing device is detached.
>>>>    */
>>>> -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr)
>>>> +struct fpga_manager *
>>>> +devm_fpga_mgr_register(struct device *dev, const char *name,
>>>     devm_fpga_mgr_register(struct device *parent,
>> Yes - thanks for catching that.
>>
>> - Russ
>>
>>>> +		       const struct fpga_manager_ops *mops, void *priv)
>>>>   {
>>>>   	struct fpga_mgr_devres *dr;
>>>> -	int ret;
>>>> -
>>>> -	/*
>>>> -	 * Make sure that the struct fpga_manager * that is passed in is
>>>> -	 * managed itself.
>>>> -	 */
>>>> -	if (WARN_ON(!devres_find(dev, devm_fpga_mgr_release,
>>>> -				 fpga_mgr_devres_match, mgr)))
>>>> -		return -EINVAL;
>>>> +	struct fpga_manager *mgr;
>>>>   
>>>>   	dr = devres_alloc(devm_fpga_mgr_unregister, sizeof(*dr), GFP_KERNEL);
>>>>   	if (!dr)
>>>> -		return -ENOMEM;
>>>> +		return ERR_PTR(-ENOMEM);
>>>>   
>>>> -	ret = fpga_mgr_register(mgr);
>>>> -	if (ret) {
>>>> +	mgr = fpga_mgr_register(dev, name, mops, priv);
>>>> +	if (IS_ERR(mgr)) {
>>>>   		devres_free(dr);
>>>> -		return ret;
>>>> +		return mgr;
>>>>   	}
>>>>   
>>>>   	dr->mgr = mgr;
>>>>   	devres_add(dev, dr);
>>>>   
>>>> -	return 0;
>>>> +	return mgr;
>>>>   }
>>>>   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)
>>>> diff --git a/drivers/fpga/ice40-spi.c b/drivers/fpga/ice40-spi.c
>>>> index 69dec5af23c3..91ac25f339f3 100644
>>>> --- a/drivers/fpga/ice40-spi.c
>>>> +++ b/drivers/fpga/ice40-spi.c
>>>> @@ -178,12 +178,12 @@ static int ice40_fpga_probe(struct spi_device *spi)
>>>>   		return ret;
>>>>   	}
>>>>   
>>>> -	mgr = devm_fpga_mgr_create(dev, "Lattice iCE40 FPGA Manager",
>>>> -				   &ice40_fpga_ops, priv);
>>>> -	if (!mgr)
>>>> -		return -ENOMEM;
>>>> +	mgr = devm_fpga_mgr_register(dev, "Lattice iCE40 FPGA Manager",
>>>> +				     &ice40_fpga_ops, priv);
>>>> +	if (IS_ERR(mgr))
>>>> +		return PTR_ERR(mgr);
>>>>   
>>>> -	return devm_fpga_mgr_register(dev, mgr);
>>>> +	return 0;
>>>>   }
>>>>   
>>>>   static const struct of_device_id ice40_fpga_of_match[] = {
>>>> diff --git a/drivers/fpga/machxo2-spi.c b/drivers/fpga/machxo2-spi.c
>>>> index 114a64d2b7a4..0137c1e2e706 100644
>>>> --- a/drivers/fpga/machxo2-spi.c
>>>> +++ b/drivers/fpga/machxo2-spi.c
>>>> @@ -366,12 +366,12 @@ static int machxo2_spi_probe(struct spi_device *spi)
>>>>   		return -EINVAL;
>>>>   	}
>>>>   
>>>> -	mgr = devm_fpga_mgr_create(dev, "Lattice MachXO2 SPI FPGA Manager",
>>>> -				   &machxo2_ops, spi);
>>>> -	if (!mgr)
>>>> -		return -ENOMEM;
>>>> +	mgr = devm_fpga_mgr_register(dev, "Lattice MachXO2 SPI FPGA Manager",
>>>> +				     &machxo2_ops, spi);
>>>> +	if (IS_ERR(mgr))
>>>> +		return PTR_ERR(mgr);
>>>>   
>>>> -	return devm_fpga_mgr_register(dev, mgr);
>>>> +	return 0;
>>>>   }
>>>>   
>>>>   static const struct of_device_id of_match[] = {
>>>> diff --git a/drivers/fpga/socfpga-a10.c b/drivers/fpga/socfpga-a10.c
>>>> index 573d88bdf730..ac8e89b8a5cc 100644
>>>> --- a/drivers/fpga/socfpga-a10.c
>>>> +++ b/drivers/fpga/socfpga-a10.c
>>>> @@ -508,19 +508,15 @@ static int socfpga_a10_fpga_probe(struct platform_device *pdev)
>>>>   		return -EBUSY;
>>>>   	}
>>>>   
>>>> -	mgr = devm_fpga_mgr_create(dev, "SoCFPGA Arria10 FPGA Manager",
>>>> -				   &socfpga_a10_fpga_mgr_ops, priv);
>>>> -	if (!mgr)
>>>> -		return -ENOMEM;
>>>> -
>>>> -	platform_set_drvdata(pdev, mgr);
>>>> -
>>>> -	ret = fpga_mgr_register(mgr);
>>>> -	if (ret) {
>>>> +	mgr = fpga_mgr_register(dev, "SoCFPGA Arria10 FPGA Manager",
>>>> +				&socfpga_a10_fpga_mgr_ops, priv);
>>>> +	if (IS_ERR(mgr)) {
>>>>   		clk_disable_unprepare(priv->clk);
>>>> -		return ret;
>>>> +		return PTR_ERR(mgr);
>>>>   	}
>>>>   
>>>> +	platform_set_drvdata(pdev, mgr);
>>>> +
>>>>   	return 0;
>>>>   }
>>>>   
>>>> diff --git a/drivers/fpga/socfpga.c b/drivers/fpga/socfpga.c
>>>> index 1f467173fc1f..ebfa9f4d24ef 100644
>>>> --- a/drivers/fpga/socfpga.c
>>>> +++ b/drivers/fpga/socfpga.c
>>>> @@ -571,12 +571,12 @@ static int socfpga_fpga_probe(struct platform_device *pdev)
>>>>   	if (ret)
>>>>   		return ret;
>>>>   
>>>> -	mgr = devm_fpga_mgr_create(dev, "Altera SOCFPGA FPGA Manager",
>>>> -				   &socfpga_fpga_ops, priv);
>>>> -	if (!mgr)
>>>> -		return -ENOMEM;
>>>> +	mgr = devm_fpga_mgr_register(dev, "Altera SOCFPGA FPGA Manager",
>>>> +				     &socfpga_fpga_ops, priv);
>>>> +	if (IS_ERR(mgr))
>>>> +		return PTR_ERR(mgr);
>>>>   
>>>> -	return devm_fpga_mgr_register(dev, mgr);
>>>> +	return 0;
>>>>   }
>>>>   
>>>>   #ifdef CONFIG_OF
>>>> diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c
>>>> index a2cea500f7cc..9155e888a133 100644
>>>> --- a/drivers/fpga/stratix10-soc.c
>>>> +++ b/drivers/fpga/stratix10-soc.c
>>>> @@ -425,18 +425,11 @@ static int s10_probe(struct platform_device *pdev)
>>>>   
>>>>   	init_completion(&priv->status_return_completion);
>>>>   
>>>> -	mgr = fpga_mgr_create(dev, "Stratix10 SOC FPGA Manager",
>>>> -			      &s10_ops, priv);
>>>> -	if (!mgr) {
>>>> -		dev_err(dev, "unable to create FPGA manager\n");
>>>> -		ret = -ENOMEM;
>>>> -		goto probe_err;
>>>> -	}
>>>> -
>>>> -	ret = fpga_mgr_register(mgr);
>>>> -	if (ret) {
>>>> +	mgr = fpga_mgr_register(dev, "Stratix10 SOC FPGA Manager",
>>>> +				&s10_ops, priv);
>>>> +	if (IS_ERR(mgr)) {
>>>>   		dev_err(dev, "unable to register FPGA manager\n");
>>>> -		fpga_mgr_free(mgr);
>>>> +		ret = PTR_ERR(mgr);
>>>>   		goto probe_err;
>>>>   	}
>>>>   
>>>> @@ -454,7 +447,6 @@ static int s10_remove(struct platform_device *pdev)
>>>>   	struct s10_priv *priv = mgr->priv;
>>>>   
>>>>   	fpga_mgr_unregister(mgr);
>>>> -	fpga_mgr_free(mgr);
>>>>   	stratix10_svc_free_channel(priv->chan);
>>>>   
>>>>   	return 0;
>>>> diff --git a/drivers/fpga/ts73xx-fpga.c b/drivers/fpga/ts73xx-fpga.c
>>>> index 101f016c6ed8..5efbec2b9f9f 100644
>>>> --- a/drivers/fpga/ts73xx-fpga.c
>>>> +++ b/drivers/fpga/ts73xx-fpga.c
>>>> @@ -122,12 +122,12 @@ static int ts73xx_fpga_probe(struct platform_device *pdev)
>>>>   	if (IS_ERR(priv->io_base))
>>>>   		return PTR_ERR(priv->io_base);
>>>>   
>>>> -	mgr = devm_fpga_mgr_create(kdev, "TS-73xx FPGA Manager",
>>>> -				   &ts73xx_fpga_ops, priv);
>>>> -	if (!mgr)
>>>> -		return -ENOMEM;
>>>> +	mgr = devm_fpga_mgr_register(kdev, "TS-73xx FPGA Manager",
>>>> +				     &ts73xx_fpga_ops, priv);
>>>> +	if (IS_ERR(mgr))
>>>> +		return PTR_ERR(mgr);
>>>>   
>>>> -	return devm_fpga_mgr_register(kdev, mgr);
>>>> +	return 0;
>>>>   }
>>>>   
>>>>   static struct platform_driver ts73xx_fpga_driver = {
>>>> diff --git a/drivers/fpga/xilinx-spi.c b/drivers/fpga/xilinx-spi.c
>>>> index fee4d0abf6bf..efda475f7a98 100644
>>>> --- a/drivers/fpga/xilinx-spi.c
>>>> +++ b/drivers/fpga/xilinx-spi.c
>>>> @@ -247,13 +247,13 @@ static int xilinx_spi_probe(struct spi_device *spi)
>>>>   		return dev_err_probe(&spi->dev, PTR_ERR(conf->done),
>>>>   				     "Failed to get DONE gpio\n");
>>>>   
>>>> -	mgr = devm_fpga_mgr_create(&spi->dev,
>>>> -				   "Xilinx Slave Serial FPGA Manager",
>>>> -				   &xilinx_spi_ops, conf);
>>>> -	if (!mgr)
>>>> -		return -ENOMEM;
>>>> +	mgr = devm_fpga_mgr_register(&spi->dev,
>>>> +				     "Xilinx Slave Serial FPGA Manager",
>>>> +				     &xilinx_spi_ops, conf);
>>>> +	if (IS_ERR(mgr))
>>>> +		return PTR_ERR(mgr);
>>>>   
>>>> -	return devm_fpga_mgr_register(&spi->dev, mgr);
>>>> +	return 0;
>>>>   }
>>>>   
>>>>   static const struct of_device_id xlnx_spi_of_match[] = {
>>>> diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c
>>>> index 9b75bd4f93d8..426aa34c6a0d 100644
>>>> --- a/drivers/fpga/zynq-fpga.c
>>>> +++ b/drivers/fpga/zynq-fpga.c
>>>> @@ -609,20 +609,16 @@ static int zynq_fpga_probe(struct platform_device *pdev)
>>>>   
>>>>   	clk_disable(priv->clk);
>>>>   
>>>> -	mgr = devm_fpga_mgr_create(dev, "Xilinx Zynq FPGA Manager",
>>>> -				   &zynq_fpga_ops, priv);
>>>> -	if (!mgr)
>>>> -		return -ENOMEM;
>>>> -
>>>> -	platform_set_drvdata(pdev, mgr);
>>>> -
>>>> -	err = fpga_mgr_register(mgr);
>>>> -	if (err) {
>>>> +	mgr = fpga_mgr_register(dev, "Xilinx Zynq FPGA Manager",
>>>> +				&zynq_fpga_ops, priv);
>>>> +	if (IS_ERR(mgr)) {
>>>>   		dev_err(dev, "unable to register FPGA manager\n");
>>>>   		clk_unprepare(priv->clk);
>>>> -		return err;
>>>> +		return PTR_ERR(mgr);
>>>>   	}
>>>>   
>>>> +	platform_set_drvdata(pdev, mgr);
>>>> +
>>>>   	return 0;
>>>>   }
>>>>   
>>>> diff --git a/drivers/fpga/zynqmp-fpga.c b/drivers/fpga/zynqmp-fpga.c
>>>> index 125743c9797f..1a6d520985be 100644
>>>> --- a/drivers/fpga/zynqmp-fpga.c
>>>> +++ b/drivers/fpga/zynqmp-fpga.c
>>>> @@ -102,12 +102,12 @@ static int zynqmp_fpga_probe(struct platform_device *pdev)
>>>>   
>>>>   	priv->dev = dev;
>>>>   
>>>> -	mgr = devm_fpga_mgr_create(dev, "Xilinx ZynqMP FPGA Manager",
>>>> -				   &zynqmp_fpga_ops, priv);
>>>> -	if (!mgr)
>>>> -		return -ENOMEM;
>>>> +	mgr = devm_fpga_mgr_register(dev, "Xilinx ZynqMP FPGA Manager",
>>>> +				     &zynqmp_fpga_ops, priv);
>>>> +	if (IS_ERR(mgr))
>>>> +		return PTR_ERR(mgr);
>>>>   
>>>> -	return devm_fpga_mgr_register(dev, mgr);
>>>> +	return 0;
>>>>   }
>>>>   
>>>>   static const struct of_device_id zynqmp_fpga_of_match[] = {
>>>> diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
>>>> index 474c1f506307..d3c6f0153457 100644
>>>> --- a/include/linux/fpga/fpga-mgr.h
>>>> +++ b/include/linux/fpga/fpga-mgr.h
>>>> @@ -105,6 +105,17 @@ struct fpga_image_info {
>>>>   #endif
>>>>   };
>>>>   
>>>> +/**
>>>> + * struct fpga_compat_id - id for compatibility check
>>>> + *
>>>> + * @id_h: high 64bit of the compat_id
>>>> + * @id_l: low 64bit of the compat_id
>>>> + */
>>>> +struct fpga_compat_id {
>>>> +	u64 id_h;
>>>> +	u64 id_l;
>>>> +};
>>>> +
>>>>   /**
>>>>    * struct fpga_manager_ops - ops for low level fpga manager drivers
>>>>    * @initial_header_size: Maximum number of bytes that should be passed into write_init
>>>> @@ -116,6 +127,7 @@ struct fpga_image_info {
>>>>    * @write_complete: set FPGA to operating state after writing is done
>>>>    * @fpga_remove: optional: Set FPGA into a specific state during driver remove
>>>>    * @groups: optional attribute groups.
>>>> + * @compat_id: optional: FPGA manager id for compatibility check.
>>>>    *
>>>>    * fpga_manager_ops are the low level functions implemented by a specific
>>>>    * fpga manager driver.  The optional ones are tested for NULL before being
>>>> @@ -134,6 +146,7 @@ struct fpga_manager_ops {
>>>>   			      struct fpga_image_info *info);
>>>>   	void (*fpga_remove)(struct fpga_manager *mgr);
>>>>   	const struct attribute_group **groups;
>>>> +	struct fpga_compat_id *compat_id;
>>> We don't have to move compat_id here, keep it in struct fpga_mgr
>>>
>>> Thanks,
>>> Yilun
>>>
>>>>   };
>>>>   
>>>>   /* FPGA manager status: Partial/Full Reconfiguration errors */
>>>> @@ -143,24 +156,12 @@ struct fpga_manager_ops {
>>>>   #define FPGA_MGR_STATUS_IP_PROTOCOL_ERR		BIT(3)
>>>>   #define FPGA_MGR_STATUS_FIFO_OVERFLOW_ERR	BIT(4)
>>>>   
>>>> -/**
>>>> - * struct fpga_compat_id - id for compatibility check
>>>> - *
>>>> - * @id_h: high 64bit of the compat_id
>>>> - * @id_l: low 64bit of the compat_id
>>>> - */
>>>> -struct fpga_compat_id {
>>>> -	u64 id_h;
>>>> -	u64 id_l;
>>>> -};
>>>> -
>>>>   /**
>>>>    * struct fpga_manager - fpga manager structure
>>>>    * @name: name of low level fpga manager
>>>>    * @dev: fpga manager device
>>>>    * @ref_mutex: only allows one reference to fpga manager
>>>>    * @state: state of fpga manager
>>>> - * @compat_id: FPGA manager id for compatibility check.
>>>>    * @mops: pointer to struct of fpga manager ops
>>>>    * @priv: low level driver private date
>>>>    */
>>>> @@ -169,7 +170,6 @@ struct fpga_manager {
>>>>   	struct device dev;
>>>>   	struct mutex ref_mutex;
>>>>   	enum fpga_mgr_states state;
>>>> -	struct fpga_compat_id *compat_id;
>>>>   	const struct fpga_manager_ops *mops;
>>>>   	void *priv;
>>>>   };
>>>> @@ -191,17 +191,14 @@ struct fpga_manager *fpga_mgr_get(struct device *dev);
>>>>   
>>>>   void fpga_mgr_put(struct fpga_manager *mgr);
>>>>   
>>>> -struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
>>>> -				     const struct fpga_manager_ops *mops,
>>>> -				     void *priv);
>>>> -void fpga_mgr_free(struct fpga_manager *mgr);
>>>> -int fpga_mgr_register(struct fpga_manager *mgr);
>>>> +struct fpga_manager *fpga_mgr_register(struct device *dev, const char *name,
>>>> +				       const struct fpga_manager_ops *mops,
>>>> +				       void *priv);
>>>>   void fpga_mgr_unregister(struct fpga_manager *mgr);
>>>>   
>>>> -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr);
>>>> -
>>>> -struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name,
>>>> -					  const struct fpga_manager_ops *mops,
>>>> -					  void *priv);
>>>> +struct fpga_manager *
>>>> +devm_fpga_mgr_register(struct device *dev, const char *name,
>>>> +		       const struct fpga_manager_ops *mops,
>>>> +		       void *priv);
>>>>   
>>>>   #endif /*_LINUX_FPGA_MGR_H */
>>>> -- 
>>>> 2.25.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v5 1/3] fpga: mgr: Use standard dev_release for class driver
  2021-06-18 20:39         ` Tom Rix
@ 2021-06-18 22:01           ` Moritz Fischer
  2021-06-21  9:48             ` Wu, Hao
  2021-06-18 22:31           ` Russ Weight
  1 sibling, 1 reply; 19+ messages in thread
From: Moritz Fischer @ 2021-06-18 22:01 UTC (permalink / raw)
  To: Tom Rix
  Cc: Russ Weight, Xu Yilun, mdf, linux-fpga, lgoncalv, hao.wu,
	matthew.gerlach, richard.gong

On Fri, Jun 18, 2021 at 01:39:42PM -0700, Tom Rix wrote:
> 
> On 6/18/21 10:58 AM, Russ Weight wrote:
> > 
> > On 6/18/21 9:03 AM, Russ Weight wrote:
> > > On 6/18/21 8:45 AM, Xu Yilun wrote:
> > > > On Wed, Jun 16, 2021 at 03:57:38PM -0700, Russ Weight wrote:
> > > > > The FPGA manager class driver data structure is being treated as a
> > > > > managed resource instead of using standard dev_release call-back
> > > > > to release the class data structure. This change removes the
> > > > > managed resource code for the freeing of the class data structure
> > > > > and combines the create() and register() functions into a single
> > > > > register() function.
> > > > > 
> > > > > The devm_fpga_mgr_register() function is retained.
> > > > > 
> > > > > Signed-off-by: Russ Weight <russell.h.weight@intel.com>
> > > > > ---
> > > > > v5:
> > > > >    - Rebased on top of recently accepted patches.
> > > > >    - Removed compat_id from the fpga_mgr_register() parameter list
> > > > >      and added it to the fpga_manager_ops structure. This also required
> > > > My reason for this change is, we could avoid further change of the
> > > > fpga_mgr_register() API if we add new input parameters later on.
> > > With this patchset, changes are only required for the callers
> > > that use the new parameters.
> > > 
> > > > >      dynamically allocating the dfl-fme-ops structure in order to add
> > > > >      the appropriate compat_id.
> > > > But enforcing the dynamical allocation of the parameters is not prefered
> > > > to me. How about a dedicated structure that wraps all the needed
> > > > parameters:
> > > > 
> > > > struct fpga_mgr_info {
> > > > 	const char *name;
> > > > 	const struct fpga_manager_ops *mops;
> > > > 	const struct fpga_compat_id *compat_id;
> > > > 	const void *priv;
> > > > };
> > > > 
> > > > Then We can simply define a local variable of this struct for
> > > > fpga_mgr_register().
> > > > 
> > > > more details inline.
> > > I agree the at the dynamic allocation is not preferred, but it is only
> > > required if compat_id is used. In all other cases, the static structure
> > > can continue to be used. In otherwords, only one caller is affected.
> > > > > v4:
> > > > >    - Added the compat_id parameter to fpga_mgr_register() and
> > > > >      devm_fpga_mgr_register() to ensure that the compat_id is set before
> > > > >      the device_register() call.
> > > > > v3:
> > > > >    - Cleaned up comment header for fpga_mgr_register()
> > > > >    - Fix error return on ida_simple_get() failure
> > > > > v2:
> > > > >    - Restored devm_fpga_mgr_register() functionality, adapted for the combined
> > > > >      create/register functionality.
> > > > >    - All previous callers of devm_fpga_mgr_register() will continue to call
> > > > >      devm_fpga_mgr_register().
> > > > >    - replaced unnecessary ternary operators in return statements with standard
> > > > >      if conditions.
> > > > > ---
> > > > >   drivers/fpga/altera-cvp.c        |  12 +--
> > > > >   drivers/fpga/altera-pr-ip-core.c |   8 +-
> > > > >   drivers/fpga/altera-ps-spi.c     |  10 +-
> > > > >   drivers/fpga/dfl-fme-mgr.c       |  52 ++++++----
> > > > >   drivers/fpga/dfl-fme-region.c    |   2 +-
> > > > >   drivers/fpga/fpga-mgr.c          | 163 ++++++++-----------------------
> > > > >   drivers/fpga/ice40-spi.c         |  10 +-
> > > > >   drivers/fpga/machxo2-spi.c       |  10 +-
> > > > >   drivers/fpga/socfpga-a10.c       |  16 ++-
> > > > >   drivers/fpga/socfpga.c           |  10 +-
> > > > >   drivers/fpga/stratix10-soc.c     |  16 +--
> > > > >   drivers/fpga/ts73xx-fpga.c       |  10 +-
> > > > >   drivers/fpga/xilinx-spi.c        |  12 +--
> > > > >   drivers/fpga/zynq-fpga.c         |  16 ++-
> > > > >   drivers/fpga/zynqmp-fpga.c       |  10 +-
> > > > >   include/linux/fpga/fpga-mgr.h    |  43 ++++----
> > > > >   16 files changed, 153 insertions(+), 247 deletions(-)
> > > > > 
> > > > > diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
> > > > > index ccf4546eff29..4ffb9da537d8 100644
> > > > > --- a/drivers/fpga/altera-cvp.c
> > > > > +++ b/drivers/fpga/altera-cvp.c
> > > > > @@ -652,19 +652,15 @@ static int altera_cvp_probe(struct pci_dev *pdev,
> > > > >   	snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s @%s",
> > > > >   		 ALTERA_CVP_MGR_NAME, pci_name(pdev));
> > > > > -	mgr = devm_fpga_mgr_create(&pdev->dev, conf->mgr_name,
> > > > > -				   &altera_cvp_ops, conf);
> > > > > -	if (!mgr) {
> > > > > -		ret = -ENOMEM;
> > > > > +	mgr = fpga_mgr_register(&pdev->dev, conf->mgr_name,
> > > > > +				&altera_cvp_ops, conf);
> > > > > +	if (IS_ERR(mgr)) {
> > > > > +		ret = PTR_ERR(mgr);
> > > > >   		goto err_unmap;
> > > > >   	}
> > > > >   	pci_set_drvdata(pdev, mgr);
> > > > > -	ret = fpga_mgr_register(mgr);
> > > > > -	if (ret)
> > > > > -		goto err_unmap;
> > > > > -
> > > > >   	return 0;
> > > > >   err_unmap:
> > > > > diff --git a/drivers/fpga/altera-pr-ip-core.c b/drivers/fpga/altera-pr-ip-core.c
> > > > > index dfdf21ed34c4..17babf974852 100644
> > > > > --- a/drivers/fpga/altera-pr-ip-core.c
> > > > > +++ b/drivers/fpga/altera-pr-ip-core.c
> > > > > @@ -191,11 +191,11 @@ int alt_pr_register(struct device *dev, void __iomem *reg_base)
> > > > >   		(val & ALT_PR_CSR_STATUS_MSK) >> ALT_PR_CSR_STATUS_SFT,
> > > > >   		(int)(val & ALT_PR_CSR_PR_START));
> > > > > -	mgr = devm_fpga_mgr_create(dev, dev_name(dev), &alt_pr_ops, priv);
> > > > > -	if (!mgr)
> > > > > -		return -ENOMEM;
> > > > > +	mgr = devm_fpga_mgr_register(dev, dev_name(dev), &alt_pr_ops, priv);
> > > > > +	if (IS_ERR(mgr))
> > > > > +		return PTR_ERR(mgr);
> > > > > -	return devm_fpga_mgr_register(dev, mgr);
> > > > > +	return 0;
> > > > >   }
> > > > >   EXPORT_SYMBOL_GPL(alt_pr_register);
> > > > > diff --git a/drivers/fpga/altera-ps-spi.c b/drivers/fpga/altera-ps-spi.c
> > > > > index 23bfd4d1ad0f..d3f77b0312b2 100644
> > > > > --- a/drivers/fpga/altera-ps-spi.c
> > > > > +++ b/drivers/fpga/altera-ps-spi.c
> > > > > @@ -302,12 +302,12 @@ static int altera_ps_probe(struct spi_device *spi)
> > > > >   	snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s %s",
> > > > >   		 dev_driver_string(&spi->dev), dev_name(&spi->dev));
> > > > > -	mgr = devm_fpga_mgr_create(&spi->dev, conf->mgr_name,
> > > > > -				   &altera_ps_ops, conf);
> > > > > -	if (!mgr)
> > > > > -		return -ENOMEM;
> > > > > +	mgr = devm_fpga_mgr_register(&spi->dev, conf->mgr_name,
> > > > > +				     &altera_ps_ops, conf);
> > > > > +	if (IS_ERR(mgr))
> > > > > +		return PTR_ERR(mgr);
> > > > > -	return devm_fpga_mgr_register(&spi->dev, mgr);
> > > > > +	return 0;
> > > > >   }
> > > > >   static const struct spi_device_id altera_ps_spi_ids[] = {
> > > > > diff --git a/drivers/fpga/dfl-fme-mgr.c b/drivers/fpga/dfl-fme-mgr.c
> > > > > index d5861d13b306..1b93bc292dbe 100644
> > > > > --- a/drivers/fpga/dfl-fme-mgr.c
> > > > > +++ b/drivers/fpga/dfl-fme-mgr.c
> > > > > @@ -264,14 +264,6 @@ static u64 fme_mgr_status(struct fpga_manager *mgr)
> > > > >   	return pr_error_to_mgr_status(priv->pr_error);
> > > > >   }
> > > > > -static const struct fpga_manager_ops fme_mgr_ops = {
> > > > > -	.write_init = fme_mgr_write_init,
> > > > > -	.write = fme_mgr_write,
> > > > > -	.write_complete = fme_mgr_write_complete,
> > > > > -	.state = fme_mgr_state,
> > > > > -	.status = fme_mgr_status,
> > > > > -};
> > > > > -
> > > > >   static void fme_mgr_get_compat_id(void __iomem *fme_pr,
> > > > >   				  struct fpga_compat_id *id)
> > > > >   {
> > > > > @@ -279,10 +271,34 @@ static void fme_mgr_get_compat_id(void __iomem *fme_pr,
> > > > >   	id->id_h = readq(fme_pr + FME_PR_INTFC_ID_H);
> > > > >   }
> > > > > +static struct fpga_manager_ops *fme_mgr_get_ops(struct device *dev,
> > > > > +						struct fme_mgr_priv *priv)
> > > > > +{
> > > > > +	struct fpga_manager_ops *ops;
> > > > > +
> > > > > +	ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL);
> > > > > +	if (!ops)
> > > > > +		return NULL;
> > > > > +
> > > > > +	ops->compat_id = devm_kzalloc(dev, sizeof(struct fpga_compat_id),
> > > > > +				      GFP_KERNEL);
> > > > > +	if (!ops->compat_id)
> > > > > +		return NULL;
> > > > > +
> > > > > +	fme_mgr_get_compat_id(priv->ioaddr, ops->compat_id);
> > > > > +	ops->write_init = fme_mgr_write_init;
> > > > > +	ops->write = fme_mgr_write;
> > > > > +	ops->write_complete = fme_mgr_write_complete;
> > > > > +	ops->state = fme_mgr_state;
> > > > > +	ops->status = fme_mgr_status;
> > > > > +
> > > > > +	return ops;
> > > > > +}
> > What do other's think? Is it better to dynamically allocate the ops structure
> > for users of compat_id (just one user at this time)? Or better to create an
> > info structure on the stack for all callers? See above for an example of a
> > dynamically allocated the ops structure.
> > 
> > To me, using the ops structure seems more standard, and the dynamic allocation,
> > while not optimal, does not require much more space or complexity than the static
> > allocation. At this time it only affects one caller.
> > 
> > Adding the info structure as a parameter to the register() functions adds a
> > little more complexity to all callers, whether or not they use the dynamic
> > elements of the structure.
> 
> Looks like dfl is the only user of compat_id.
> 
> A board specific value does not belong in a common structure, it belongs in
> a board structure
> 
> Move compat_id out of fpga-mgr.h and into dfl.h
> 
> In dfl- you can do whatever you want.

Agreed. You'll have to deal with DFL specific region, that directly
accesses mgr->compat_id.

But yes, this should move to DFL, nobody else is using it.

- Moritz

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v5 1/3] fpga: mgr: Use standard dev_release for class driver
  2021-06-18 20:39         ` Tom Rix
  2021-06-18 22:01           ` Moritz Fischer
@ 2021-06-18 22:31           ` Russ Weight
  2021-06-21  6:39             ` Xu Yilun
  1 sibling, 1 reply; 19+ messages in thread
From: Russ Weight @ 2021-06-18 22:31 UTC (permalink / raw)
  To: Tom Rix, Xu Yilun
  Cc: mdf, linux-fpga, lgoncalv, hao.wu, matthew.gerlach, richard.gong



On 6/18/21 1:39 PM, Tom Rix wrote:
>
> On 6/18/21 10:58 AM, Russ Weight wrote:
>>
>> On 6/18/21 9:03 AM, Russ Weight wrote:
>>> On 6/18/21 8:45 AM, Xu Yilun wrote:
>>>> On Wed, Jun 16, 2021 at 03:57:38PM -0700, Russ Weight wrote:
>>>>> The FPGA manager class driver data structure is being treated as a
>>>>> managed resource instead of using standard dev_release call-back
>>>>> to release the class data structure. This change removes the
>>>>> managed resource code for the freeing of the class data structure
>>>>> and combines the create() and register() functions into a single
>>>>> register() function.
>>>>>
>>>>> The devm_fpga_mgr_register() function is retained.
>>>>>
>>>>> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
>>>>> ---
>>>>> v5:
>>>>>    - Rebased on top of recently accepted patches.
>>>>>    - Removed compat_id from the fpga_mgr_register() parameter list
>>>>>      and added it to the fpga_manager_ops structure. This also required
>>>> My reason for this change is, we could avoid further change of the
>>>> fpga_mgr_register() API if we add new input parameters later on.
>>> With this patchset, changes are only required for the callers
>>> that use the new parameters.
>>>
>>>>>      dynamically allocating the dfl-fme-ops structure in order to add
>>>>>      the appropriate compat_id.
>>>> But enforcing the dynamical allocation of the parameters is not prefered
>>>> to me. How about a dedicated structure that wraps all the needed
>>>> parameters:
>>>>
>>>> struct fpga_mgr_info {
>>>>     const char *name;
>>>>     const struct fpga_manager_ops *mops;
>>>>     const struct fpga_compat_id *compat_id;
>>>>     const void *priv;
>>>> };
>>>>
>>>> Then We can simply define a local variable of this struct for
>>>> fpga_mgr_register().
>>>>
>>>> more details inline.
>>> I agree the at the dynamic allocation is not preferred, but it is only
>>> required if compat_id is used. In all other cases, the static structure
>>> can continue to be used. In otherwords, only one caller is affected.
>>>>> v4:
>>>>>    - Added the compat_id parameter to fpga_mgr_register() and
>>>>>      devm_fpga_mgr_register() to ensure that the compat_id is set before
>>>>>      the device_register() call.
>>>>> v3:
>>>>>    - Cleaned up comment header for fpga_mgr_register()
>>>>>    - Fix error return on ida_simple_get() failure
>>>>> v2:
>>>>>    - Restored devm_fpga_mgr_register() functionality, adapted for the combined
>>>>>      create/register functionality.
>>>>>    - All previous callers of devm_fpga_mgr_register() will continue to call
>>>>>      devm_fpga_mgr_register().
>>>>>    - replaced unnecessary ternary operators in return statements with standard
>>>>>      if conditions.
>>>>> ---
>>>>>   drivers/fpga/altera-cvp.c        |  12 +--
>>>>>   drivers/fpga/altera-pr-ip-core.c |   8 +-
>>>>>   drivers/fpga/altera-ps-spi.c     |  10 +-
>>>>>   drivers/fpga/dfl-fme-mgr.c       |  52 ++++++----
>>>>>   drivers/fpga/dfl-fme-region.c    |   2 +-
>>>>>   drivers/fpga/fpga-mgr.c          | 163 ++++++++-----------------------
>>>>>   drivers/fpga/ice40-spi.c         |  10 +-
>>>>>   drivers/fpga/machxo2-spi.c       |  10 +-
>>>>>   drivers/fpga/socfpga-a10.c       |  16 ++-
>>>>>   drivers/fpga/socfpga.c           |  10 +-
>>>>>   drivers/fpga/stratix10-soc.c     |  16 +--
>>>>>   drivers/fpga/ts73xx-fpga.c       |  10 +-
>>>>>   drivers/fpga/xilinx-spi.c        |  12 +--
>>>>>   drivers/fpga/zynq-fpga.c         |  16 ++-
>>>>>   drivers/fpga/zynqmp-fpga.c       |  10 +-
>>>>>   include/linux/fpga/fpga-mgr.h    |  43 ++++----
>>>>>   16 files changed, 153 insertions(+), 247 deletions(-)
>>>>>
>>>>> diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
>>>>> index ccf4546eff29..4ffb9da537d8 100644
>>>>> --- a/drivers/fpga/altera-cvp.c
>>>>> +++ b/drivers/fpga/altera-cvp.c
>>>>> @@ -652,19 +652,15 @@ static int altera_cvp_probe(struct pci_dev *pdev,
>>>>>       snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s @%s",
>>>>>            ALTERA_CVP_MGR_NAME, pci_name(pdev));
>>>>>   -    mgr = devm_fpga_mgr_create(&pdev->dev, conf->mgr_name,
>>>>> -                   &altera_cvp_ops, conf);
>>>>> -    if (!mgr) {
>>>>> -        ret = -ENOMEM;
>>>>> +    mgr = fpga_mgr_register(&pdev->dev, conf->mgr_name,
>>>>> +                &altera_cvp_ops, conf);
>>>>> +    if (IS_ERR(mgr)) {
>>>>> +        ret = PTR_ERR(mgr);
>>>>>           goto err_unmap;
>>>>>       }
>>>>>         pci_set_drvdata(pdev, mgr);
>>>>>   -    ret = fpga_mgr_register(mgr);
>>>>> -    if (ret)
>>>>> -        goto err_unmap;
>>>>> -
>>>>>       return 0;
>>>>>     err_unmap:
>>>>> diff --git a/drivers/fpga/altera-pr-ip-core.c b/drivers/fpga/altera-pr-ip-core.c
>>>>> index dfdf21ed34c4..17babf974852 100644
>>>>> --- a/drivers/fpga/altera-pr-ip-core.c
>>>>> +++ b/drivers/fpga/altera-pr-ip-core.c
>>>>> @@ -191,11 +191,11 @@ int alt_pr_register(struct device *dev, void __iomem *reg_base)
>>>>>           (val & ALT_PR_CSR_STATUS_MSK) >> ALT_PR_CSR_STATUS_SFT,
>>>>>           (int)(val & ALT_PR_CSR_PR_START));
>>>>>   -    mgr = devm_fpga_mgr_create(dev, dev_name(dev), &alt_pr_ops, priv);
>>>>> -    if (!mgr)
>>>>> -        return -ENOMEM;
>>>>> +    mgr = devm_fpga_mgr_register(dev, dev_name(dev), &alt_pr_ops, priv);
>>>>> +    if (IS_ERR(mgr))
>>>>> +        return PTR_ERR(mgr);
>>>>>   -    return devm_fpga_mgr_register(dev, mgr);
>>>>> +    return 0;
>>>>>   }
>>>>>   EXPORT_SYMBOL_GPL(alt_pr_register);
>>>>>   diff --git a/drivers/fpga/altera-ps-spi.c b/drivers/fpga/altera-ps-spi.c
>>>>> index 23bfd4d1ad0f..d3f77b0312b2 100644
>>>>> --- a/drivers/fpga/altera-ps-spi.c
>>>>> +++ b/drivers/fpga/altera-ps-spi.c
>>>>> @@ -302,12 +302,12 @@ static int altera_ps_probe(struct spi_device *spi)
>>>>>       snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s %s",
>>>>>            dev_driver_string(&spi->dev), dev_name(&spi->dev));
>>>>>   -    mgr = devm_fpga_mgr_create(&spi->dev, conf->mgr_name,
>>>>> -                   &altera_ps_ops, conf);
>>>>> -    if (!mgr)
>>>>> -        return -ENOMEM;
>>>>> +    mgr = devm_fpga_mgr_register(&spi->dev, conf->mgr_name,
>>>>> +                     &altera_ps_ops, conf);
>>>>> +    if (IS_ERR(mgr))
>>>>> +        return PTR_ERR(mgr);
>>>>>   -    return devm_fpga_mgr_register(&spi->dev, mgr);
>>>>> +    return 0;
>>>>>   }
>>>>>     static const struct spi_device_id altera_ps_spi_ids[] = {
>>>>> diff --git a/drivers/fpga/dfl-fme-mgr.c b/drivers/fpga/dfl-fme-mgr.c
>>>>> index d5861d13b306..1b93bc292dbe 100644
>>>>> --- a/drivers/fpga/dfl-fme-mgr.c
>>>>> +++ b/drivers/fpga/dfl-fme-mgr.c
>>>>> @@ -264,14 +264,6 @@ static u64 fme_mgr_status(struct fpga_manager *mgr)
>>>>>       return pr_error_to_mgr_status(priv->pr_error);
>>>>>   }
>>>>>   -static const struct fpga_manager_ops fme_mgr_ops = {
>>>>> -    .write_init = fme_mgr_write_init,
>>>>> -    .write = fme_mgr_write,
>>>>> -    .write_complete = fme_mgr_write_complete,
>>>>> -    .state = fme_mgr_state,
>>>>> -    .status = fme_mgr_status,
>>>>> -};
>>>>> -
>>>>>   static void fme_mgr_get_compat_id(void __iomem *fme_pr,
>>>>>                     struct fpga_compat_id *id)
>>>>>   {
>>>>> @@ -279,10 +271,34 @@ static void fme_mgr_get_compat_id(void __iomem *fme_pr,
>>>>>       id->id_h = readq(fme_pr + FME_PR_INTFC_ID_H);
>>>>>   }
>>>>>   +static struct fpga_manager_ops *fme_mgr_get_ops(struct device *dev,
>>>>> +                        struct fme_mgr_priv *priv)
>>>>> +{
>>>>> +    struct fpga_manager_ops *ops;
>>>>> +
>>>>> +    ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL);
>>>>> +    if (!ops)
>>>>> +        return NULL;
>>>>> +
>>>>> +    ops->compat_id = devm_kzalloc(dev, sizeof(struct fpga_compat_id),
>>>>> +                      GFP_KERNEL);
>>>>> +    if (!ops->compat_id)
>>>>> +        return NULL;
>>>>> +
>>>>> +    fme_mgr_get_compat_id(priv->ioaddr, ops->compat_id);
>>>>> +    ops->write_init = fme_mgr_write_init;
>>>>> +    ops->write = fme_mgr_write;
>>>>> +    ops->write_complete = fme_mgr_write_complete;
>>>>> +    ops->state = fme_mgr_state;
>>>>> +    ops->status = fme_mgr_status;
>>>>> +
>>>>> +    return ops;
>>>>> +}
>> What do other's think? Is it better to dynamically allocate the ops structure
>> for users of compat_id (just one user at this time)? Or better to create an
>> info structure on the stack for all callers? See above for an example of a
>> dynamically allocated the ops structure.
>>
>> To me, using the ops structure seems more standard, and the dynamic allocation,
>> while not optimal, does not require much more space or complexity than the static
>> allocation. At this time it only affects one caller.
>>
>> Adding the info structure as a parameter to the register() functions adds a
>> little more complexity to all callers, whether or not they use the dynamic
>> elements of the structure.
>
> Looks like dfl is the only user of compat_id.
>
> A board specific value does not belong in a common structure, it belongs in a board structure
>
> Move compat_id out of fpga-mgr.h and into dfl.h
>
> In dfl- you can do whatever you want.

Yilun - is this a correct explanation of the use of compat_id?

I believe the compat_id is used to verify whether or not an AFU is compatible with the Static Region. When the FPGA Manager is created (for PR purposes) the compat_id is read from the Static Region and stored in the FPGA Manager. When the region is created (containing the PR region), the same compat_id is then associated with the FPGA Region. The compat_id is exported by the region via syfs to allow userspace to verify compatibility between an AFU and the static region.

And I suppose the FPGA Manager _could_ be associated with multiple regions?

So I think compat_id _is_ (optionally) an attribute of the FPGA manager and the Region.

- Russ

>
> And leave the others' ops as-is.
>
>
> Tom
>
>>
>> - Russ
>>
>>>>> +
>>>>>   static int fme_mgr_probe(struct platform_device *pdev)
>>>>>   {
>>>>>       struct dfl_fme_mgr_pdata *pdata = dev_get_platdata(&pdev->dev);
>>>>> -    struct fpga_compat_id *compat_id;
>>>>> +    struct fpga_manager_ops *fme_mgr_ops;
>>>>>       struct device *dev = &pdev->dev;
>>>>>       struct fme_mgr_priv *priv;
>>>>>       struct fpga_manager *mgr;
>>>>> @@ -302,20 +318,16 @@ static int fme_mgr_probe(struct platform_device *pdev)
>>>>>               return PTR_ERR(priv->ioaddr);
>>>>>       }
>>>>>   -    compat_id = devm_kzalloc(dev, sizeof(*compat_id), GFP_KERNEL);
>>>>> -    if (!compat_id)
>>>>> -        return -ENOMEM;
>>>>> -
>>>>> -    fme_mgr_get_compat_id(priv->ioaddr, compat_id);
>>>>> -
>>>>> -    mgr = devm_fpga_mgr_create(dev, "DFL FME FPGA Manager",
>>>>> -                   &fme_mgr_ops, priv);
>>>>> -    if (!mgr)
>>>>> +    fme_mgr_ops = fme_mgr_get_ops(dev, priv);
>>>>> +    if (!fme_mgr_ops)
>>>>>           return -ENOMEM;
>>>>>   -    mgr->compat_id = compat_id;
>>>>> +    mgr = devm_fpga_mgr_register(dev, "DFL FME FPGA Manager",
>>>>> +                     fme_mgr_ops, priv);
>>>>> +    if (IS_ERR(mgr))
>>>>> +        return PTR_ERR(mgr);
>>>> With structure fpga_mgr_info, we don't need the dynamical allocation
>>>> anymore:
>>>>
>>>>     struct fpga_mgr_info minfo = { 0 };
>>>>
>>>>     minfo.name = "DFL FME FPGA Manager";
>>>>     minfo.mops = &fme_mgr_ops;
>>>>     minfo.compat_id = compat_id;
>>>>     minfo.priv = priv;
>>>>
>>>>     mgr = devm_fpga_mgr_register(dev, &minfo);
>>>>
>>>> Drivers could also leaves minfo.compat_id zero if they don't care.
>>>>
>>>> I also expect more opinions.
>>>>
>>>>>   -    return devm_fpga_mgr_register(dev, mgr);
>>>>> +    return 0;
>>>>>   }
>>>>>     static struct platform_driver fme_mgr_driver = {
>>>>> diff --git a/drivers/fpga/dfl-fme-region.c b/drivers/fpga/dfl-fme-region.c
>>>>> index 1eeb42af1012..a64c13e198a0 100644
>>>>> --- a/drivers/fpga/dfl-fme-region.c
>>>>> +++ b/drivers/fpga/dfl-fme-region.c
>>>>> @@ -46,7 +46,7 @@ static int fme_region_probe(struct platform_device *pdev)
>>>>>       }
>>>>>         region->priv = pdata;
>>>>> -    region->compat_id = mgr->compat_id;
>>>>> +    region->compat_id = mgr->mops->compat_id;
>>>> The changes should be in Patch #3, is it?
>>> This is the patch that moves compat_id out of mgr and into mgr->mops.
>>> In order to be able to compile, this patch needs the change.
>>>
>>>>>       platform_set_drvdata(pdev, region);
>>>>>         ret = fpga_region_register(region);
>>>>> diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
>>>>> index ecb4c3c795fa..9aecfea432c8 100644
>>>>> --- a/drivers/fpga/fpga-mgr.c
>>>>> +++ b/drivers/fpga/fpga-mgr.c
>>>>> @@ -550,20 +550,20 @@ void fpga_mgr_unlock(struct fpga_manager *mgr)
>>>>>   EXPORT_SYMBOL_GPL(fpga_mgr_unlock);
>>>>>     /**
>>>>> - * fpga_mgr_create - create and initialize an FPGA manager struct
>>>>> + * 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
>>>>>    *
>>>>> - * 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 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 *parent, 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)
>>>> fpga_mgr_register(struct device *parent, struct fpga_mgr_info *minfo)
>>>>
>>>>>   {
>>>>>       struct fpga_manager *mgr;
>>>>>       int id, ret;
>>>>> @@ -572,21 +572,23 @@ struct fpga_manager *fpga_mgr_create(struct device *parent, const char *name,
>>>>>           !mops->write_init || (!mops->write && !mops->write_sg) ||
>>>>>           (mops->write && mops->write_sg)) {
>>>>>           dev_err(parent, "Attempt to register without fpga_manager_ops\n");
>>>>> -        return NULL;
>>>>> +        return ERR_PTR(-EINVAL);
>>>>>       }
>>>>>         if (!name || !strlen(name)) {
>>>>>           dev_err(parent, "Attempt to register with no name!\n");
>>>>> -        return NULL;
>>>>> +        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)
>>>>> +    if (id < 0) {
>>>>> +        ret = id;
>>>>>           goto error_kfree;
>>>>> +    }
>>>>>         mutex_init(&mgr->ref_mutex);
>>>>>   @@ -594,7 +596,6 @@ struct fpga_manager *fpga_mgr_create(struct device *parent, const char *name,
>>>>>       mgr->mops = mops;
>>>>>       mgr->priv = priv;
>>>>     mgr->name = minfo->name;
>>>>     mgr->mops = minfo->mops;
>>>>     mgr->priv = minfo->priv;
>>>>     mgr->compat_id = minfo->compat_id;
>>>>
>>>>>   -    device_initialize(&mgr->dev);
>>>>>       mgr->dev.class = fpga_mgr_class;
>>>>>       mgr->dev.groups = mops->groups;
>>>>>       mgr->dev.parent = parent;
>>>>> @@ -605,84 +606,6 @@ struct fpga_manager *fpga_mgr_create(struct device *parent, const char *name,
>>>>>       if (ret)
>>>>>           goto error_device;
>>>>>   -    return mgr;
>>>>> -
>>>>> -error_device:
>>>>> -    ida_simple_remove(&fpga_mgr_ida, id);
>>>>> -error_kfree:
>>>>> -    kfree(mgr);
>>>>> -
>>>>> -    return NULL;
>>>>> -}
>>>>> -EXPORT_SYMBOL_GPL(fpga_mgr_create);
>>>>> -
>>>>> -/**
>>>>> - * fpga_mgr_free - free an 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_mgr_devres *dr = res;
>>>>> -
>>>>> -    fpga_mgr_free(dr->mgr);
>>>>> -}
>>>>> -
>>>>> -/**
>>>>> - * devm_fpga_mgr_create - create and initialize a managed FPGA manager struct
>>>>> - * @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 an 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.
>>>>> - *
>>>>> - * Return: pointer to struct fpga_manager or NULL
>>>>> - */
>>>>> -struct fpga_manager *devm_fpga_mgr_create(struct device *parent, const char *name,
>>>>> -                      const struct fpga_manager_ops *mops,
>>>>> -                      void *priv)
>>>>> -{
>>>>> -    struct fpga_mgr_devres *dr;
>>>>> -
>>>>> -    dr = devres_alloc(devm_fpga_mgr_release, sizeof(*dr), GFP_KERNEL);
>>>>> -    if (!dr)
>>>>> -        return NULL;
>>>>> -
>>>>> -    dr->mgr = fpga_mgr_create(parent, name, mops, priv);
>>>>> -    if (!dr->mgr) {
>>>>> -        devres_free(dr);
>>>>> -        return NULL;
>>>>> -    }
>>>>> -
>>>>> -    devres_add(parent, dr);
>>>>> -
>>>>> -    return dr->mgr;
>>>>> -}
>>>>> -EXPORT_SYMBOL_GPL(devm_fpga_mgr_create);
>>>>> -
>>>>> -/**
>>>>> - * fpga_mgr_register - register an FPGA manager
>>>>> - * @mgr: fpga manager struct
>>>>> - *
>>>>> - * Return: 0 on success, negative error code otherwise.
>>>>> - */
>>>>> -int fpga_mgr_register(struct fpga_manager *mgr)
>>>>> -{
>>>>> -    int ret;
>>>>> -
>>>>>       /*
>>>>>        * Initialize framework state by requesting low level driver read state
>>>>>        * from device.  FPGA may be in reset mode or may have been programmed
>>>>> @@ -690,18 +613,20 @@ int fpga_mgr_register(struct fpga_manager *mgr)
>>>>>        */
>>>>>       mgr->state = mgr->mops->state(mgr);
>>>>>   -    ret = device_add(&mgr->dev);
>>>>> -    if (ret)
>>>>> -        goto error_device;
>>>>> -
>>>>> -    dev_info(&mgr->dev, "%s registered\n", mgr->name);
>>>>> +    ret = device_register(&mgr->dev);
>>>>> +    if (ret) {
>>>>> +        put_device(&mgr->dev);
>>>>> +        return ERR_PTR(ret);
>>>>> +    }
>>>>>   -    return 0;
>>>>> +    return mgr;
>>>>>     error_device:
>>>>> -    ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
>>>>> +    ida_simple_remove(&fpga_mgr_ida, id);
>>>>> +error_kfree:
>>>>> +    kfree(mgr);
>>>>>   -    return ret;
>>>>> +    return ERR_PTR(ret);
>>>>>   }
>>>>>   EXPORT_SYMBOL_GPL(fpga_mgr_register);
>>>>>   @@ -726,14 +651,6 @@ void fpga_mgr_unregister(struct fpga_manager *mgr)
>>>>>   }
>>>>>   EXPORT_SYMBOL_GPL(fpga_mgr_unregister);
>>>>>   -static int fpga_mgr_devres_match(struct device *dev, void *res,
>>>>> -                 void *match_data)
>>>>> -{
>>>>> -    struct fpga_mgr_devres *dr = res;
>>>>> -
>>>>> -    return match_data == dr->mgr;
>>>>> -}
>>>>> -
>>>>>   static void devm_fpga_mgr_unregister(struct device *dev, void *res)
>>>>>   {
>>>>>       struct fpga_mgr_devres *dr = res;
>>>>> @@ -743,44 +660,44 @@ static void devm_fpga_mgr_unregister(struct device *dev, void *res)
>>>>>     /**
>>>>>    * devm_fpga_mgr_register - resource managed variant of fpga_mgr_register()
>>>>> - * @dev: managing device for this FPGA manager
>>>>> - * @mgr: 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
>>>>>    *
>>>>>    * This is the devres variant of fpga_mgr_register() for which the unregister
>>>>>    * function will be called automatically when the managing device is detached.
>>>>>    */
>>>>> -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr)
>>>>> +struct fpga_manager *
>>>>> +devm_fpga_mgr_register(struct device *dev, const char *name,
>>>>     devm_fpga_mgr_register(struct device *parent,
>>> Yes - thanks for catching that.
>>>
>>> - Russ
>>>
>>>>> +               const struct fpga_manager_ops *mops, void *priv)
>>>>>   {
>>>>>       struct fpga_mgr_devres *dr;
>>>>> -    int ret;
>>>>> -
>>>>> -    /*
>>>>> -     * Make sure that the struct fpga_manager * that is passed in is
>>>>> -     * managed itself.
>>>>> -     */
>>>>> -    if (WARN_ON(!devres_find(dev, devm_fpga_mgr_release,
>>>>> -                 fpga_mgr_devres_match, mgr)))
>>>>> -        return -EINVAL;
>>>>> +    struct fpga_manager *mgr;
>>>>>         dr = devres_alloc(devm_fpga_mgr_unregister, sizeof(*dr), GFP_KERNEL);
>>>>>       if (!dr)
>>>>> -        return -ENOMEM;
>>>>> +        return ERR_PTR(-ENOMEM);
>>>>>   -    ret = fpga_mgr_register(mgr);
>>>>> -    if (ret) {
>>>>> +    mgr = fpga_mgr_register(dev, name, mops, priv);
>>>>> +    if (IS_ERR(mgr)) {
>>>>>           devres_free(dr);
>>>>> -        return ret;
>>>>> +        return mgr;
>>>>>       }
>>>>>         dr->mgr = mgr;
>>>>>       devres_add(dev, dr);
>>>>>   -    return 0;
>>>>> +    return mgr;
>>>>>   }
>>>>>   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)
>>>>> diff --git a/drivers/fpga/ice40-spi.c b/drivers/fpga/ice40-spi.c
>>>>> index 69dec5af23c3..91ac25f339f3 100644
>>>>> --- a/drivers/fpga/ice40-spi.c
>>>>> +++ b/drivers/fpga/ice40-spi.c
>>>>> @@ -178,12 +178,12 @@ static int ice40_fpga_probe(struct spi_device *spi)
>>>>>           return ret;
>>>>>       }
>>>>>   -    mgr = devm_fpga_mgr_create(dev, "Lattice iCE40 FPGA Manager",
>>>>> -                   &ice40_fpga_ops, priv);
>>>>> -    if (!mgr)
>>>>> -        return -ENOMEM;
>>>>> +    mgr = devm_fpga_mgr_register(dev, "Lattice iCE40 FPGA Manager",
>>>>> +                     &ice40_fpga_ops, priv);
>>>>> +    if (IS_ERR(mgr))
>>>>> +        return PTR_ERR(mgr);
>>>>>   -    return devm_fpga_mgr_register(dev, mgr);
>>>>> +    return 0;
>>>>>   }
>>>>>     static const struct of_device_id ice40_fpga_of_match[] = {
>>>>> diff --git a/drivers/fpga/machxo2-spi.c b/drivers/fpga/machxo2-spi.c
>>>>> index 114a64d2b7a4..0137c1e2e706 100644
>>>>> --- a/drivers/fpga/machxo2-spi.c
>>>>> +++ b/drivers/fpga/machxo2-spi.c
>>>>> @@ -366,12 +366,12 @@ static int machxo2_spi_probe(struct spi_device *spi)
>>>>>           return -EINVAL;
>>>>>       }
>>>>>   -    mgr = devm_fpga_mgr_create(dev, "Lattice MachXO2 SPI FPGA Manager",
>>>>> -                   &machxo2_ops, spi);
>>>>> -    if (!mgr)
>>>>> -        return -ENOMEM;
>>>>> +    mgr = devm_fpga_mgr_register(dev, "Lattice MachXO2 SPI FPGA Manager",
>>>>> +                     &machxo2_ops, spi);
>>>>> +    if (IS_ERR(mgr))
>>>>> +        return PTR_ERR(mgr);
>>>>>   -    return devm_fpga_mgr_register(dev, mgr);
>>>>> +    return 0;
>>>>>   }
>>>>>     static const struct of_device_id of_match[] = {
>>>>> diff --git a/drivers/fpga/socfpga-a10.c b/drivers/fpga/socfpga-a10.c
>>>>> index 573d88bdf730..ac8e89b8a5cc 100644
>>>>> --- a/drivers/fpga/socfpga-a10.c
>>>>> +++ b/drivers/fpga/socfpga-a10.c
>>>>> @@ -508,19 +508,15 @@ static int socfpga_a10_fpga_probe(struct platform_device *pdev)
>>>>>           return -EBUSY;
>>>>>       }
>>>>>   -    mgr = devm_fpga_mgr_create(dev, "SoCFPGA Arria10 FPGA Manager",
>>>>> -                   &socfpga_a10_fpga_mgr_ops, priv);
>>>>> -    if (!mgr)
>>>>> -        return -ENOMEM;
>>>>> -
>>>>> -    platform_set_drvdata(pdev, mgr);
>>>>> -
>>>>> -    ret = fpga_mgr_register(mgr);
>>>>> -    if (ret) {
>>>>> +    mgr = fpga_mgr_register(dev, "SoCFPGA Arria10 FPGA Manager",
>>>>> +                &socfpga_a10_fpga_mgr_ops, priv);
>>>>> +    if (IS_ERR(mgr)) {
>>>>>           clk_disable_unprepare(priv->clk);
>>>>> -        return ret;
>>>>> +        return PTR_ERR(mgr);
>>>>>       }
>>>>>   +    platform_set_drvdata(pdev, mgr);
>>>>> +
>>>>>       return 0;
>>>>>   }
>>>>>   diff --git a/drivers/fpga/socfpga.c b/drivers/fpga/socfpga.c
>>>>> index 1f467173fc1f..ebfa9f4d24ef 100644
>>>>> --- a/drivers/fpga/socfpga.c
>>>>> +++ b/drivers/fpga/socfpga.c
>>>>> @@ -571,12 +571,12 @@ static int socfpga_fpga_probe(struct platform_device *pdev)
>>>>>       if (ret)
>>>>>           return ret;
>>>>>   -    mgr = devm_fpga_mgr_create(dev, "Altera SOCFPGA FPGA Manager",
>>>>> -                   &socfpga_fpga_ops, priv);
>>>>> -    if (!mgr)
>>>>> -        return -ENOMEM;
>>>>> +    mgr = devm_fpga_mgr_register(dev, "Altera SOCFPGA FPGA Manager",
>>>>> +                     &socfpga_fpga_ops, priv);
>>>>> +    if (IS_ERR(mgr))
>>>>> +        return PTR_ERR(mgr);
>>>>>   -    return devm_fpga_mgr_register(dev, mgr);
>>>>> +    return 0;
>>>>>   }
>>>>>     #ifdef CONFIG_OF
>>>>> diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c
>>>>> index a2cea500f7cc..9155e888a133 100644
>>>>> --- a/drivers/fpga/stratix10-soc.c
>>>>> +++ b/drivers/fpga/stratix10-soc.c
>>>>> @@ -425,18 +425,11 @@ static int s10_probe(struct platform_device *pdev)
>>>>>         init_completion(&priv->status_return_completion);
>>>>>   -    mgr = fpga_mgr_create(dev, "Stratix10 SOC FPGA Manager",
>>>>> -                  &s10_ops, priv);
>>>>> -    if (!mgr) {
>>>>> -        dev_err(dev, "unable to create FPGA manager\n");
>>>>> -        ret = -ENOMEM;
>>>>> -        goto probe_err;
>>>>> -    }
>>>>> -
>>>>> -    ret = fpga_mgr_register(mgr);
>>>>> -    if (ret) {
>>>>> +    mgr = fpga_mgr_register(dev, "Stratix10 SOC FPGA Manager",
>>>>> +                &s10_ops, priv);
>>>>> +    if (IS_ERR(mgr)) {
>>>>>           dev_err(dev, "unable to register FPGA manager\n");
>>>>> -        fpga_mgr_free(mgr);
>>>>> +        ret = PTR_ERR(mgr);
>>>>>           goto probe_err;
>>>>>       }
>>>>>   @@ -454,7 +447,6 @@ static int s10_remove(struct platform_device *pdev)
>>>>>       struct s10_priv *priv = mgr->priv;
>>>>>         fpga_mgr_unregister(mgr);
>>>>> -    fpga_mgr_free(mgr);
>>>>>       stratix10_svc_free_channel(priv->chan);
>>>>>         return 0;
>>>>> diff --git a/drivers/fpga/ts73xx-fpga.c b/drivers/fpga/ts73xx-fpga.c
>>>>> index 101f016c6ed8..5efbec2b9f9f 100644
>>>>> --- a/drivers/fpga/ts73xx-fpga.c
>>>>> +++ b/drivers/fpga/ts73xx-fpga.c
>>>>> @@ -122,12 +122,12 @@ static int ts73xx_fpga_probe(struct platform_device *pdev)
>>>>>       if (IS_ERR(priv->io_base))
>>>>>           return PTR_ERR(priv->io_base);
>>>>>   -    mgr = devm_fpga_mgr_create(kdev, "TS-73xx FPGA Manager",
>>>>> -                   &ts73xx_fpga_ops, priv);
>>>>> -    if (!mgr)
>>>>> -        return -ENOMEM;
>>>>> +    mgr = devm_fpga_mgr_register(kdev, "TS-73xx FPGA Manager",
>>>>> +                     &ts73xx_fpga_ops, priv);
>>>>> +    if (IS_ERR(mgr))
>>>>> +        return PTR_ERR(mgr);
>>>>>   -    return devm_fpga_mgr_register(kdev, mgr);
>>>>> +    return 0;
>>>>>   }
>>>>>     static struct platform_driver ts73xx_fpga_driver = {
>>>>> diff --git a/drivers/fpga/xilinx-spi.c b/drivers/fpga/xilinx-spi.c
>>>>> index fee4d0abf6bf..efda475f7a98 100644
>>>>> --- a/drivers/fpga/xilinx-spi.c
>>>>> +++ b/drivers/fpga/xilinx-spi.c
>>>>> @@ -247,13 +247,13 @@ static int xilinx_spi_probe(struct spi_device *spi)
>>>>>           return dev_err_probe(&spi->dev, PTR_ERR(conf->done),
>>>>>                        "Failed to get DONE gpio\n");
>>>>>   -    mgr = devm_fpga_mgr_create(&spi->dev,
>>>>> -                   "Xilinx Slave Serial FPGA Manager",
>>>>> -                   &xilinx_spi_ops, conf);
>>>>> -    if (!mgr)
>>>>> -        return -ENOMEM;
>>>>> +    mgr = devm_fpga_mgr_register(&spi->dev,
>>>>> +                     "Xilinx Slave Serial FPGA Manager",
>>>>> +                     &xilinx_spi_ops, conf);
>>>>> +    if (IS_ERR(mgr))
>>>>> +        return PTR_ERR(mgr);
>>>>>   -    return devm_fpga_mgr_register(&spi->dev, mgr);
>>>>> +    return 0;
>>>>>   }
>>>>>     static const struct of_device_id xlnx_spi_of_match[] = {
>>>>> diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c
>>>>> index 9b75bd4f93d8..426aa34c6a0d 100644
>>>>> --- a/drivers/fpga/zynq-fpga.c
>>>>> +++ b/drivers/fpga/zynq-fpga.c
>>>>> @@ -609,20 +609,16 @@ static int zynq_fpga_probe(struct platform_device *pdev)
>>>>>         clk_disable(priv->clk);
>>>>>   -    mgr = devm_fpga_mgr_create(dev, "Xilinx Zynq FPGA Manager",
>>>>> -                   &zynq_fpga_ops, priv);
>>>>> -    if (!mgr)
>>>>> -        return -ENOMEM;
>>>>> -
>>>>> -    platform_set_drvdata(pdev, mgr);
>>>>> -
>>>>> -    err = fpga_mgr_register(mgr);
>>>>> -    if (err) {
>>>>> +    mgr = fpga_mgr_register(dev, "Xilinx Zynq FPGA Manager",
>>>>> +                &zynq_fpga_ops, priv);
>>>>> +    if (IS_ERR(mgr)) {
>>>>>           dev_err(dev, "unable to register FPGA manager\n");
>>>>>           clk_unprepare(priv->clk);
>>>>> -        return err;
>>>>> +        return PTR_ERR(mgr);
>>>>>       }
>>>>>   +    platform_set_drvdata(pdev, mgr);
>>>>> +
>>>>>       return 0;
>>>>>   }
>>>>>   diff --git a/drivers/fpga/zynqmp-fpga.c b/drivers/fpga/zynqmp-fpga.c
>>>>> index 125743c9797f..1a6d520985be 100644
>>>>> --- a/drivers/fpga/zynqmp-fpga.c
>>>>> +++ b/drivers/fpga/zynqmp-fpga.c
>>>>> @@ -102,12 +102,12 @@ static int zynqmp_fpga_probe(struct platform_device *pdev)
>>>>>         priv->dev = dev;
>>>>>   -    mgr = devm_fpga_mgr_create(dev, "Xilinx ZynqMP FPGA Manager",
>>>>> -                   &zynqmp_fpga_ops, priv);
>>>>> -    if (!mgr)
>>>>> -        return -ENOMEM;
>>>>> +    mgr = devm_fpga_mgr_register(dev, "Xilinx ZynqMP FPGA Manager",
>>>>> +                     &zynqmp_fpga_ops, priv);
>>>>> +    if (IS_ERR(mgr))
>>>>> +        return PTR_ERR(mgr);
>>>>>   -    return devm_fpga_mgr_register(dev, mgr);
>>>>> +    return 0;
>>>>>   }
>>>>>     static const struct of_device_id zynqmp_fpga_of_match[] = {
>>>>> diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
>>>>> index 474c1f506307..d3c6f0153457 100644
>>>>> --- a/include/linux/fpga/fpga-mgr.h
>>>>> +++ b/include/linux/fpga/fpga-mgr.h
>>>>> @@ -105,6 +105,17 @@ struct fpga_image_info {
>>>>>   #endif
>>>>>   };
>>>>>   +/**
>>>>> + * struct fpga_compat_id - id for compatibility check
>>>>> + *
>>>>> + * @id_h: high 64bit of the compat_id
>>>>> + * @id_l: low 64bit of the compat_id
>>>>> + */
>>>>> +struct fpga_compat_id {
>>>>> +    u64 id_h;
>>>>> +    u64 id_l;
>>>>> +};
>>>>> +
>>>>>   /**
>>>>>    * struct fpga_manager_ops - ops for low level fpga manager drivers
>>>>>    * @initial_header_size: Maximum number of bytes that should be passed into write_init
>>>>> @@ -116,6 +127,7 @@ struct fpga_image_info {
>>>>>    * @write_complete: set FPGA to operating state after writing is done
>>>>>    * @fpga_remove: optional: Set FPGA into a specific state during driver remove
>>>>>    * @groups: optional attribute groups.
>>>>> + * @compat_id: optional: FPGA manager id for compatibility check.
>>>>>    *
>>>>>    * fpga_manager_ops are the low level functions implemented by a specific
>>>>>    * fpga manager driver.  The optional ones are tested for NULL before being
>>>>> @@ -134,6 +146,7 @@ struct fpga_manager_ops {
>>>>>                     struct fpga_image_info *info);
>>>>>       void (*fpga_remove)(struct fpga_manager *mgr);
>>>>>       const struct attribute_group **groups;
>>>>> +    struct fpga_compat_id *compat_id;
>>>> We don't have to move compat_id here, keep it in struct fpga_mgr
>>>>
>>>> Thanks,
>>>> Yilun
>>>>
>>>>>   };
>>>>>     /* FPGA manager status: Partial/Full Reconfiguration errors */
>>>>> @@ -143,24 +156,12 @@ struct fpga_manager_ops {
>>>>>   #define FPGA_MGR_STATUS_IP_PROTOCOL_ERR        BIT(3)
>>>>>   #define FPGA_MGR_STATUS_FIFO_OVERFLOW_ERR    BIT(4)
>>>>>   -/**
>>>>> - * struct fpga_compat_id - id for compatibility check
>>>>> - *
>>>>> - * @id_h: high 64bit of the compat_id
>>>>> - * @id_l: low 64bit of the compat_id
>>>>> - */
>>>>> -struct fpga_compat_id {
>>>>> -    u64 id_h;
>>>>> -    u64 id_l;
>>>>> -};
>>>>> -
>>>>>   /**
>>>>>    * struct fpga_manager - fpga manager structure
>>>>>    * @name: name of low level fpga manager
>>>>>    * @dev: fpga manager device
>>>>>    * @ref_mutex: only allows one reference to fpga manager
>>>>>    * @state: state of fpga manager
>>>>> - * @compat_id: FPGA manager id for compatibility check.
>>>>>    * @mops: pointer to struct of fpga manager ops
>>>>>    * @priv: low level driver private date
>>>>>    */
>>>>> @@ -169,7 +170,6 @@ struct fpga_manager {
>>>>>       struct device dev;
>>>>>       struct mutex ref_mutex;
>>>>>       enum fpga_mgr_states state;
>>>>> -    struct fpga_compat_id *compat_id;
>>>>>       const struct fpga_manager_ops *mops;
>>>>>       void *priv;
>>>>>   };
>>>>> @@ -191,17 +191,14 @@ struct fpga_manager *fpga_mgr_get(struct device *dev);
>>>>>     void fpga_mgr_put(struct fpga_manager *mgr);
>>>>>   -struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
>>>>> -                     const struct fpga_manager_ops *mops,
>>>>> -                     void *priv);
>>>>> -void fpga_mgr_free(struct fpga_manager *mgr);
>>>>> -int fpga_mgr_register(struct fpga_manager *mgr);
>>>>> +struct fpga_manager *fpga_mgr_register(struct device *dev, const char *name,
>>>>> +                       const struct fpga_manager_ops *mops,
>>>>> +                       void *priv);
>>>>>   void fpga_mgr_unregister(struct fpga_manager *mgr);
>>>>>   -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr);
>>>>> -
>>>>> -struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name,
>>>>> -                      const struct fpga_manager_ops *mops,
>>>>> -                      void *priv);
>>>>> +struct fpga_manager *
>>>>> +devm_fpga_mgr_register(struct device *dev, const char *name,
>>>>> +               const struct fpga_manager_ops *mops,
>>>>> +               void *priv);
>>>>>     #endif /*_LINUX_FPGA_MGR_H */
>>>>> -- 
>>>>> 2.25.1
>


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v5 1/3] fpga: mgr: Use standard dev_release for class driver
  2021-06-18 22:31           ` Russ Weight
@ 2021-06-21  6:39             ` Xu Yilun
  0 siblings, 0 replies; 19+ messages in thread
From: Xu Yilun @ 2021-06-21  6:39 UTC (permalink / raw)
  To: Russ Weight
  Cc: Tom Rix, mdf, linux-fpga, lgoncalv, hao.wu, matthew.gerlach,
	richard.gong

On Fri, Jun 18, 2021 at 03:31:50PM -0700, Russ Weight wrote:
> 
> 
> On 6/18/21 1:39 PM, Tom Rix wrote:
> >
> > On 6/18/21 10:58 AM, Russ Weight wrote:
> >>
> >> On 6/18/21 9:03 AM, Russ Weight wrote:
> >>> On 6/18/21 8:45 AM, Xu Yilun wrote:
> >>>> On Wed, Jun 16, 2021 at 03:57:38PM -0700, Russ Weight wrote:
> >>>>> The FPGA manager class driver data structure is being treated as a
> >>>>> managed resource instead of using standard dev_release call-back
> >>>>> to release the class data structure. This change removes the
> >>>>> managed resource code for the freeing of the class data structure
> >>>>> and combines the create() and register() functions into a single
> >>>>> register() function.
> >>>>>
> >>>>> The devm_fpga_mgr_register() function is retained.
> >>>>>
> >>>>> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
> >>>>> ---
> >>>>> v5:
> >>>>>    - Rebased on top of recently accepted patches.
> >>>>>    - Removed compat_id from the fpga_mgr_register() parameter list
> >>>>>      and added it to the fpga_manager_ops structure. This also required
> >>>> My reason for this change is, we could avoid further change of the
> >>>> fpga_mgr_register() API if we add new input parameters later on.
> >>> With this patchset, changes are only required for the callers
> >>> that use the new parameters.
> >>>
> >>>>>      dynamically allocating the dfl-fme-ops structure in order to add
> >>>>>      the appropriate compat_id.
> >>>> But enforcing the dynamical allocation of the parameters is not prefered
> >>>> to me. How about a dedicated structure that wraps all the needed
> >>>> parameters:
> >>>>
> >>>> struct fpga_mgr_info {
> >>>>     const char *name;
> >>>>     const struct fpga_manager_ops *mops;
> >>>>     const struct fpga_compat_id *compat_id;
> >>>>     const void *priv;
> >>>> };
> >>>>
> >>>> Then We can simply define a local variable of this struct for
> >>>> fpga_mgr_register().
> >>>>
> >>>> more details inline.
> >>> I agree the at the dynamic allocation is not preferred, but it is only
> >>> required if compat_id is used. In all other cases, the static structure
> >>> can continue to be used. In otherwords, only one caller is affected.
> >>>>> v4:
> >>>>>    - Added the compat_id parameter to fpga_mgr_register() and
> >>>>>      devm_fpga_mgr_register() to ensure that the compat_id is set before
> >>>>>      the device_register() call.
> >>>>> v3:
> >>>>>    - Cleaned up comment header for fpga_mgr_register()
> >>>>>    - Fix error return on ida_simple_get() failure
> >>>>> v2:
> >>>>>    - Restored devm_fpga_mgr_register() functionality, adapted for the combined
> >>>>>      create/register functionality.
> >>>>>    - All previous callers of devm_fpga_mgr_register() will continue to call
> >>>>>      devm_fpga_mgr_register().
> >>>>>    - replaced unnecessary ternary operators in return statements with standard
> >>>>>      if conditions.
> >>>>> ---
> >>>>>   drivers/fpga/altera-cvp.c        |  12 +--
> >>>>>   drivers/fpga/altera-pr-ip-core.c |   8 +-
> >>>>>   drivers/fpga/altera-ps-spi.c     |  10 +-
> >>>>>   drivers/fpga/dfl-fme-mgr.c       |  52 ++++++----
> >>>>>   drivers/fpga/dfl-fme-region.c    |   2 +-
> >>>>>   drivers/fpga/fpga-mgr.c          | 163 ++++++++-----------------------
> >>>>>   drivers/fpga/ice40-spi.c         |  10 +-
> >>>>>   drivers/fpga/machxo2-spi.c       |  10 +-
> >>>>>   drivers/fpga/socfpga-a10.c       |  16 ++-
> >>>>>   drivers/fpga/socfpga.c           |  10 +-
> >>>>>   drivers/fpga/stratix10-soc.c     |  16 +--
> >>>>>   drivers/fpga/ts73xx-fpga.c       |  10 +-
> >>>>>   drivers/fpga/xilinx-spi.c        |  12 +--
> >>>>>   drivers/fpga/zynq-fpga.c         |  16 ++-
> >>>>>   drivers/fpga/zynqmp-fpga.c       |  10 +-
> >>>>>   include/linux/fpga/fpga-mgr.h    |  43 ++++----
> >>>>>   16 files changed, 153 insertions(+), 247 deletions(-)
> >>>>>
> >>>>> diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
> >>>>> index ccf4546eff29..4ffb9da537d8 100644
> >>>>> --- a/drivers/fpga/altera-cvp.c
> >>>>> +++ b/drivers/fpga/altera-cvp.c
> >>>>> @@ -652,19 +652,15 @@ static int altera_cvp_probe(struct pci_dev *pdev,
> >>>>>       snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s @%s",
> >>>>>            ALTERA_CVP_MGR_NAME, pci_name(pdev));
> >>>>>   -    mgr = devm_fpga_mgr_create(&pdev->dev, conf->mgr_name,
> >>>>> -                   &altera_cvp_ops, conf);
> >>>>> -    if (!mgr) {
> >>>>> -        ret = -ENOMEM;
> >>>>> +    mgr = fpga_mgr_register(&pdev->dev, conf->mgr_name,
> >>>>> +                &altera_cvp_ops, conf);
> >>>>> +    if (IS_ERR(mgr)) {
> >>>>> +        ret = PTR_ERR(mgr);
> >>>>>           goto err_unmap;
> >>>>>       }
> >>>>>         pci_set_drvdata(pdev, mgr);
> >>>>>   -    ret = fpga_mgr_register(mgr);
> >>>>> -    if (ret)
> >>>>> -        goto err_unmap;
> >>>>> -
> >>>>>       return 0;
> >>>>>     err_unmap:
> >>>>> diff --git a/drivers/fpga/altera-pr-ip-core.c b/drivers/fpga/altera-pr-ip-core.c
> >>>>> index dfdf21ed34c4..17babf974852 100644
> >>>>> --- a/drivers/fpga/altera-pr-ip-core.c
> >>>>> +++ b/drivers/fpga/altera-pr-ip-core.c
> >>>>> @@ -191,11 +191,11 @@ int alt_pr_register(struct device *dev, void __iomem *reg_base)
> >>>>>           (val & ALT_PR_CSR_STATUS_MSK) >> ALT_PR_CSR_STATUS_SFT,
> >>>>>           (int)(val & ALT_PR_CSR_PR_START));
> >>>>>   -    mgr = devm_fpga_mgr_create(dev, dev_name(dev), &alt_pr_ops, priv);
> >>>>> -    if (!mgr)
> >>>>> -        return -ENOMEM;
> >>>>> +    mgr = devm_fpga_mgr_register(dev, dev_name(dev), &alt_pr_ops, priv);
> >>>>> +    if (IS_ERR(mgr))
> >>>>> +        return PTR_ERR(mgr);
> >>>>>   -    return devm_fpga_mgr_register(dev, mgr);
> >>>>> +    return 0;
> >>>>>   }
> >>>>>   EXPORT_SYMBOL_GPL(alt_pr_register);
> >>>>>   diff --git a/drivers/fpga/altera-ps-spi.c b/drivers/fpga/altera-ps-spi.c
> >>>>> index 23bfd4d1ad0f..d3f77b0312b2 100644
> >>>>> --- a/drivers/fpga/altera-ps-spi.c
> >>>>> +++ b/drivers/fpga/altera-ps-spi.c
> >>>>> @@ -302,12 +302,12 @@ static int altera_ps_probe(struct spi_device *spi)
> >>>>>       snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s %s",
> >>>>>            dev_driver_string(&spi->dev), dev_name(&spi->dev));
> >>>>>   -    mgr = devm_fpga_mgr_create(&spi->dev, conf->mgr_name,
> >>>>> -                   &altera_ps_ops, conf);
> >>>>> -    if (!mgr)
> >>>>> -        return -ENOMEM;
> >>>>> +    mgr = devm_fpga_mgr_register(&spi->dev, conf->mgr_name,
> >>>>> +                     &altera_ps_ops, conf);
> >>>>> +    if (IS_ERR(mgr))
> >>>>> +        return PTR_ERR(mgr);
> >>>>>   -    return devm_fpga_mgr_register(&spi->dev, mgr);
> >>>>> +    return 0;
> >>>>>   }
> >>>>>     static const struct spi_device_id altera_ps_spi_ids[] = {
> >>>>> diff --git a/drivers/fpga/dfl-fme-mgr.c b/drivers/fpga/dfl-fme-mgr.c
> >>>>> index d5861d13b306..1b93bc292dbe 100644
> >>>>> --- a/drivers/fpga/dfl-fme-mgr.c
> >>>>> +++ b/drivers/fpga/dfl-fme-mgr.c
> >>>>> @@ -264,14 +264,6 @@ static u64 fme_mgr_status(struct fpga_manager *mgr)
> >>>>>       return pr_error_to_mgr_status(priv->pr_error);
> >>>>>   }
> >>>>>   -static const struct fpga_manager_ops fme_mgr_ops = {
> >>>>> -    .write_init = fme_mgr_write_init,
> >>>>> -    .write = fme_mgr_write,
> >>>>> -    .write_complete = fme_mgr_write_complete,
> >>>>> -    .state = fme_mgr_state,
> >>>>> -    .status = fme_mgr_status,
> >>>>> -};
> >>>>> -
> >>>>>   static void fme_mgr_get_compat_id(void __iomem *fme_pr,
> >>>>>                     struct fpga_compat_id *id)
> >>>>>   {
> >>>>> @@ -279,10 +271,34 @@ static void fme_mgr_get_compat_id(void __iomem *fme_pr,
> >>>>>       id->id_h = readq(fme_pr + FME_PR_INTFC_ID_H);
> >>>>>   }
> >>>>>   +static struct fpga_manager_ops *fme_mgr_get_ops(struct device *dev,
> >>>>> +                        struct fme_mgr_priv *priv)
> >>>>> +{
> >>>>> +    struct fpga_manager_ops *ops;
> >>>>> +
> >>>>> +    ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL);
> >>>>> +    if (!ops)
> >>>>> +        return NULL;
> >>>>> +
> >>>>> +    ops->compat_id = devm_kzalloc(dev, sizeof(struct fpga_compat_id),
> >>>>> +                      GFP_KERNEL);
> >>>>> +    if (!ops->compat_id)
> >>>>> +        return NULL;
> >>>>> +
> >>>>> +    fme_mgr_get_compat_id(priv->ioaddr, ops->compat_id);
> >>>>> +    ops->write_init = fme_mgr_write_init;
> >>>>> +    ops->write = fme_mgr_write;
> >>>>> +    ops->write_complete = fme_mgr_write_complete;
> >>>>> +    ops->state = fme_mgr_state;
> >>>>> +    ops->status = fme_mgr_status;
> >>>>> +
> >>>>> +    return ops;
> >>>>> +}
> >> What do other's think? Is it better to dynamically allocate the ops structure
> >> for users of compat_id (just one user at this time)? Or better to create an
> >> info structure on the stack for all callers? See above for an example of a
> >> dynamically allocated the ops structure.
> >>
> >> To me, using the ops structure seems more standard, and the dynamic allocation,
> >> while not optimal, does not require much more space or complexity than the static
> >> allocation. At this time it only affects one caller.
> >>
> >> Adding the info structure as a parameter to the register() functions adds a
> >> little more complexity to all callers, whether or not they use the dynamic
> >> elements of the structure.
> >
> > Looks like dfl is the only user of compat_id.
> >
> > A board specific value does not belong in a common structure, it belongs in a board structure
> >
> > Move compat_id out of fpga-mgr.h and into dfl.h
> >
> > In dfl- you can do whatever you want.
> 
> Yilun - is this a correct explanation of the use of compat_id?
> 
> I believe the compat_id is used to verify whether or not an AFU is compatible with the Static Region. When the FPGA Manager is created (for PR purposes) the compat_id is read from the Static Region and stored in the FPGA Manager. When the region is created (containing the PR region), the same compat_id is then associated with the FPGA Region. The compat_id is exported by the region via syfs to allow userspace to verify compatibility between an AFU and the static region.
> 
> And I suppose the FPGA Manager _could_ be associated with multiple regions?
> 
> So I think compat_id _is_ (optionally) an attribute of the FPGA manager and the Region.

Yes, I believe it could be associated with FPGA mgr & region dev. It is
for compatibility check of the FPGA reconfiguration hardware and image.
Maybe we don't move it away just because DFL is the only one who
implements this interface.

Thanks,
Yilun

> 
> - Russ
> 
> >
> > And leave the others' ops as-is.
> >
> >
> > Tom
> >
> >>
> >> - Russ
> >>
> >>>>> +
> >>>>>   static int fme_mgr_probe(struct platform_device *pdev)
> >>>>>   {
> >>>>>       struct dfl_fme_mgr_pdata *pdata = dev_get_platdata(&pdev->dev);
> >>>>> -    struct fpga_compat_id *compat_id;
> >>>>> +    struct fpga_manager_ops *fme_mgr_ops;
> >>>>>       struct device *dev = &pdev->dev;
> >>>>>       struct fme_mgr_priv *priv;
> >>>>>       struct fpga_manager *mgr;
> >>>>> @@ -302,20 +318,16 @@ static int fme_mgr_probe(struct platform_device *pdev)
> >>>>>               return PTR_ERR(priv->ioaddr);
> >>>>>       }
> >>>>>   -    compat_id = devm_kzalloc(dev, sizeof(*compat_id), GFP_KERNEL);
> >>>>> -    if (!compat_id)
> >>>>> -        return -ENOMEM;
> >>>>> -
> >>>>> -    fme_mgr_get_compat_id(priv->ioaddr, compat_id);
> >>>>> -
> >>>>> -    mgr = devm_fpga_mgr_create(dev, "DFL FME FPGA Manager",
> >>>>> -                   &fme_mgr_ops, priv);
> >>>>> -    if (!mgr)
> >>>>> +    fme_mgr_ops = fme_mgr_get_ops(dev, priv);
> >>>>> +    if (!fme_mgr_ops)
> >>>>>           return -ENOMEM;
> >>>>>   -    mgr->compat_id = compat_id;
> >>>>> +    mgr = devm_fpga_mgr_register(dev, "DFL FME FPGA Manager",
> >>>>> +                     fme_mgr_ops, priv);
> >>>>> +    if (IS_ERR(mgr))
> >>>>> +        return PTR_ERR(mgr);
> >>>> With structure fpga_mgr_info, we don't need the dynamical allocation
> >>>> anymore:
> >>>>
> >>>>     struct fpga_mgr_info minfo = { 0 };
> >>>>
> >>>>     minfo.name = "DFL FME FPGA Manager";
> >>>>     minfo.mops = &fme_mgr_ops;
> >>>>     minfo.compat_id = compat_id;
> >>>>     minfo.priv = priv;
> >>>>
> >>>>     mgr = devm_fpga_mgr_register(dev, &minfo);
> >>>>
> >>>> Drivers could also leaves minfo.compat_id zero if they don't care.
> >>>>
> >>>> I also expect more opinions.
> >>>>
> >>>>>   -    return devm_fpga_mgr_register(dev, mgr);
> >>>>> +    return 0;
> >>>>>   }
> >>>>>     static struct platform_driver fme_mgr_driver = {
> >>>>> diff --git a/drivers/fpga/dfl-fme-region.c b/drivers/fpga/dfl-fme-region.c
> >>>>> index 1eeb42af1012..a64c13e198a0 100644
> >>>>> --- a/drivers/fpga/dfl-fme-region.c
> >>>>> +++ b/drivers/fpga/dfl-fme-region.c
> >>>>> @@ -46,7 +46,7 @@ static int fme_region_probe(struct platform_device *pdev)
> >>>>>       }
> >>>>>         region->priv = pdata;
> >>>>> -    region->compat_id = mgr->compat_id;
> >>>>> +    region->compat_id = mgr->mops->compat_id;
> >>>> The changes should be in Patch #3, is it?
> >>> This is the patch that moves compat_id out of mgr and into mgr->mops.
> >>> In order to be able to compile, this patch needs the change.
> >>>
> >>>>>       platform_set_drvdata(pdev, region);
> >>>>>         ret = fpga_region_register(region);
> >>>>> diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
> >>>>> index ecb4c3c795fa..9aecfea432c8 100644
> >>>>> --- a/drivers/fpga/fpga-mgr.c
> >>>>> +++ b/drivers/fpga/fpga-mgr.c
> >>>>> @@ -550,20 +550,20 @@ void fpga_mgr_unlock(struct fpga_manager *mgr)
> >>>>>   EXPORT_SYMBOL_GPL(fpga_mgr_unlock);
> >>>>>     /**
> >>>>> - * fpga_mgr_create - create and initialize an FPGA manager struct
> >>>>> + * 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
> >>>>>    *
> >>>>> - * 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 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 *parent, 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)
> >>>> fpga_mgr_register(struct device *parent, struct fpga_mgr_info *minfo)
> >>>>
> >>>>>   {
> >>>>>       struct fpga_manager *mgr;
> >>>>>       int id, ret;
> >>>>> @@ -572,21 +572,23 @@ struct fpga_manager *fpga_mgr_create(struct device *parent, const char *name,
> >>>>>           !mops->write_init || (!mops->write && !mops->write_sg) ||
> >>>>>           (mops->write && mops->write_sg)) {
> >>>>>           dev_err(parent, "Attempt to register without fpga_manager_ops\n");
> >>>>> -        return NULL;
> >>>>> +        return ERR_PTR(-EINVAL);
> >>>>>       }
> >>>>>         if (!name || !strlen(name)) {
> >>>>>           dev_err(parent, "Attempt to register with no name!\n");
> >>>>> -        return NULL;
> >>>>> +        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)
> >>>>> +    if (id < 0) {
> >>>>> +        ret = id;
> >>>>>           goto error_kfree;
> >>>>> +    }
> >>>>>         mutex_init(&mgr->ref_mutex);
> >>>>>   @@ -594,7 +596,6 @@ struct fpga_manager *fpga_mgr_create(struct device *parent, const char *name,
> >>>>>       mgr->mops = mops;
> >>>>>       mgr->priv = priv;
> >>>>     mgr->name = minfo->name;
> >>>>     mgr->mops = minfo->mops;
> >>>>     mgr->priv = minfo->priv;
> >>>>     mgr->compat_id = minfo->compat_id;
> >>>>
> >>>>>   -    device_initialize(&mgr->dev);
> >>>>>       mgr->dev.class = fpga_mgr_class;
> >>>>>       mgr->dev.groups = mops->groups;
> >>>>>       mgr->dev.parent = parent;
> >>>>> @@ -605,84 +606,6 @@ struct fpga_manager *fpga_mgr_create(struct device *parent, const char *name,
> >>>>>       if (ret)
> >>>>>           goto error_device;
> >>>>>   -    return mgr;
> >>>>> -
> >>>>> -error_device:
> >>>>> -    ida_simple_remove(&fpga_mgr_ida, id);
> >>>>> -error_kfree:
> >>>>> -    kfree(mgr);
> >>>>> -
> >>>>> -    return NULL;
> >>>>> -}
> >>>>> -EXPORT_SYMBOL_GPL(fpga_mgr_create);
> >>>>> -
> >>>>> -/**
> >>>>> - * fpga_mgr_free - free an 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_mgr_devres *dr = res;
> >>>>> -
> >>>>> -    fpga_mgr_free(dr->mgr);
> >>>>> -}
> >>>>> -
> >>>>> -/**
> >>>>> - * devm_fpga_mgr_create - create and initialize a managed FPGA manager struct
> >>>>> - * @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 an 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.
> >>>>> - *
> >>>>> - * Return: pointer to struct fpga_manager or NULL
> >>>>> - */
> >>>>> -struct fpga_manager *devm_fpga_mgr_create(struct device *parent, const char *name,
> >>>>> -                      const struct fpga_manager_ops *mops,
> >>>>> -                      void *priv)
> >>>>> -{
> >>>>> -    struct fpga_mgr_devres *dr;
> >>>>> -
> >>>>> -    dr = devres_alloc(devm_fpga_mgr_release, sizeof(*dr), GFP_KERNEL);
> >>>>> -    if (!dr)
> >>>>> -        return NULL;
> >>>>> -
> >>>>> -    dr->mgr = fpga_mgr_create(parent, name, mops, priv);
> >>>>> -    if (!dr->mgr) {
> >>>>> -        devres_free(dr);
> >>>>> -        return NULL;
> >>>>> -    }
> >>>>> -
> >>>>> -    devres_add(parent, dr);
> >>>>> -
> >>>>> -    return dr->mgr;
> >>>>> -}
> >>>>> -EXPORT_SYMBOL_GPL(devm_fpga_mgr_create);
> >>>>> -
> >>>>> -/**
> >>>>> - * fpga_mgr_register - register an FPGA manager
> >>>>> - * @mgr: fpga manager struct
> >>>>> - *
> >>>>> - * Return: 0 on success, negative error code otherwise.
> >>>>> - */
> >>>>> -int fpga_mgr_register(struct fpga_manager *mgr)
> >>>>> -{
> >>>>> -    int ret;
> >>>>> -
> >>>>>       /*
> >>>>>        * Initialize framework state by requesting low level driver read state
> >>>>>        * from device.  FPGA may be in reset mode or may have been programmed
> >>>>> @@ -690,18 +613,20 @@ int fpga_mgr_register(struct fpga_manager *mgr)
> >>>>>        */
> >>>>>       mgr->state = mgr->mops->state(mgr);
> >>>>>   -    ret = device_add(&mgr->dev);
> >>>>> -    if (ret)
> >>>>> -        goto error_device;
> >>>>> -
> >>>>> -    dev_info(&mgr->dev, "%s registered\n", mgr->name);
> >>>>> +    ret = device_register(&mgr->dev);
> >>>>> +    if (ret) {
> >>>>> +        put_device(&mgr->dev);
> >>>>> +        return ERR_PTR(ret);
> >>>>> +    }
> >>>>>   -    return 0;
> >>>>> +    return mgr;
> >>>>>     error_device:
> >>>>> -    ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
> >>>>> +    ida_simple_remove(&fpga_mgr_ida, id);
> >>>>> +error_kfree:
> >>>>> +    kfree(mgr);
> >>>>>   -    return ret;
> >>>>> +    return ERR_PTR(ret);
> >>>>>   }
> >>>>>   EXPORT_SYMBOL_GPL(fpga_mgr_register);
> >>>>>   @@ -726,14 +651,6 @@ void fpga_mgr_unregister(struct fpga_manager *mgr)
> >>>>>   }
> >>>>>   EXPORT_SYMBOL_GPL(fpga_mgr_unregister);
> >>>>>   -static int fpga_mgr_devres_match(struct device *dev, void *res,
> >>>>> -                 void *match_data)
> >>>>> -{
> >>>>> -    struct fpga_mgr_devres *dr = res;
> >>>>> -
> >>>>> -    return match_data == dr->mgr;
> >>>>> -}
> >>>>> -
> >>>>>   static void devm_fpga_mgr_unregister(struct device *dev, void *res)
> >>>>>   {
> >>>>>       struct fpga_mgr_devres *dr = res;
> >>>>> @@ -743,44 +660,44 @@ static void devm_fpga_mgr_unregister(struct device *dev, void *res)
> >>>>>     /**
> >>>>>    * devm_fpga_mgr_register - resource managed variant of fpga_mgr_register()
> >>>>> - * @dev: managing device for this FPGA manager
> >>>>> - * @mgr: 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
> >>>>>    *
> >>>>>    * This is the devres variant of fpga_mgr_register() for which the unregister
> >>>>>    * function will be called automatically when the managing device is detached.
> >>>>>    */
> >>>>> -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr)
> >>>>> +struct fpga_manager *
> >>>>> +devm_fpga_mgr_register(struct device *dev, const char *name,
> >>>>     devm_fpga_mgr_register(struct device *parent,
> >>> Yes - thanks for catching that.
> >>>
> >>> - Russ
> >>>
> >>>>> +               const struct fpga_manager_ops *mops, void *priv)
> >>>>>   {
> >>>>>       struct fpga_mgr_devres *dr;
> >>>>> -    int ret;
> >>>>> -
> >>>>> -    /*
> >>>>> -     * Make sure that the struct fpga_manager * that is passed in is
> >>>>> -     * managed itself.
> >>>>> -     */
> >>>>> -    if (WARN_ON(!devres_find(dev, devm_fpga_mgr_release,
> >>>>> -                 fpga_mgr_devres_match, mgr)))
> >>>>> -        return -EINVAL;
> >>>>> +    struct fpga_manager *mgr;
> >>>>>         dr = devres_alloc(devm_fpga_mgr_unregister, sizeof(*dr), GFP_KERNEL);
> >>>>>       if (!dr)
> >>>>> -        return -ENOMEM;
> >>>>> +        return ERR_PTR(-ENOMEM);
> >>>>>   -    ret = fpga_mgr_register(mgr);
> >>>>> -    if (ret) {
> >>>>> +    mgr = fpga_mgr_register(dev, name, mops, priv);
> >>>>> +    if (IS_ERR(mgr)) {
> >>>>>           devres_free(dr);
> >>>>> -        return ret;
> >>>>> +        return mgr;
> >>>>>       }
> >>>>>         dr->mgr = mgr;
> >>>>>       devres_add(dev, dr);
> >>>>>   -    return 0;
> >>>>> +    return mgr;
> >>>>>   }
> >>>>>   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)
> >>>>> diff --git a/drivers/fpga/ice40-spi.c b/drivers/fpga/ice40-spi.c
> >>>>> index 69dec5af23c3..91ac25f339f3 100644
> >>>>> --- a/drivers/fpga/ice40-spi.c
> >>>>> +++ b/drivers/fpga/ice40-spi.c
> >>>>> @@ -178,12 +178,12 @@ static int ice40_fpga_probe(struct spi_device *spi)
> >>>>>           return ret;
> >>>>>       }
> >>>>>   -    mgr = devm_fpga_mgr_create(dev, "Lattice iCE40 FPGA Manager",
> >>>>> -                   &ice40_fpga_ops, priv);
> >>>>> -    if (!mgr)
> >>>>> -        return -ENOMEM;
> >>>>> +    mgr = devm_fpga_mgr_register(dev, "Lattice iCE40 FPGA Manager",
> >>>>> +                     &ice40_fpga_ops, priv);
> >>>>> +    if (IS_ERR(mgr))
> >>>>> +        return PTR_ERR(mgr);
> >>>>>   -    return devm_fpga_mgr_register(dev, mgr);
> >>>>> +    return 0;
> >>>>>   }
> >>>>>     static const struct of_device_id ice40_fpga_of_match[] = {
> >>>>> diff --git a/drivers/fpga/machxo2-spi.c b/drivers/fpga/machxo2-spi.c
> >>>>> index 114a64d2b7a4..0137c1e2e706 100644
> >>>>> --- a/drivers/fpga/machxo2-spi.c
> >>>>> +++ b/drivers/fpga/machxo2-spi.c
> >>>>> @@ -366,12 +366,12 @@ static int machxo2_spi_probe(struct spi_device *spi)
> >>>>>           return -EINVAL;
> >>>>>       }
> >>>>>   -    mgr = devm_fpga_mgr_create(dev, "Lattice MachXO2 SPI FPGA Manager",
> >>>>> -                   &machxo2_ops, spi);
> >>>>> -    if (!mgr)
> >>>>> -        return -ENOMEM;
> >>>>> +    mgr = devm_fpga_mgr_register(dev, "Lattice MachXO2 SPI FPGA Manager",
> >>>>> +                     &machxo2_ops, spi);
> >>>>> +    if (IS_ERR(mgr))
> >>>>> +        return PTR_ERR(mgr);
> >>>>>   -    return devm_fpga_mgr_register(dev, mgr);
> >>>>> +    return 0;
> >>>>>   }
> >>>>>     static const struct of_device_id of_match[] = {
> >>>>> diff --git a/drivers/fpga/socfpga-a10.c b/drivers/fpga/socfpga-a10.c
> >>>>> index 573d88bdf730..ac8e89b8a5cc 100644
> >>>>> --- a/drivers/fpga/socfpga-a10.c
> >>>>> +++ b/drivers/fpga/socfpga-a10.c
> >>>>> @@ -508,19 +508,15 @@ static int socfpga_a10_fpga_probe(struct platform_device *pdev)
> >>>>>           return -EBUSY;
> >>>>>       }
> >>>>>   -    mgr = devm_fpga_mgr_create(dev, "SoCFPGA Arria10 FPGA Manager",
> >>>>> -                   &socfpga_a10_fpga_mgr_ops, priv);
> >>>>> -    if (!mgr)
> >>>>> -        return -ENOMEM;
> >>>>> -
> >>>>> -    platform_set_drvdata(pdev, mgr);
> >>>>> -
> >>>>> -    ret = fpga_mgr_register(mgr);
> >>>>> -    if (ret) {
> >>>>> +    mgr = fpga_mgr_register(dev, "SoCFPGA Arria10 FPGA Manager",
> >>>>> +                &socfpga_a10_fpga_mgr_ops, priv);
> >>>>> +    if (IS_ERR(mgr)) {
> >>>>>           clk_disable_unprepare(priv->clk);
> >>>>> -        return ret;
> >>>>> +        return PTR_ERR(mgr);
> >>>>>       }
> >>>>>   +    platform_set_drvdata(pdev, mgr);
> >>>>> +
> >>>>>       return 0;
> >>>>>   }
> >>>>>   diff --git a/drivers/fpga/socfpga.c b/drivers/fpga/socfpga.c
> >>>>> index 1f467173fc1f..ebfa9f4d24ef 100644
> >>>>> --- a/drivers/fpga/socfpga.c
> >>>>> +++ b/drivers/fpga/socfpga.c
> >>>>> @@ -571,12 +571,12 @@ static int socfpga_fpga_probe(struct platform_device *pdev)
> >>>>>       if (ret)
> >>>>>           return ret;
> >>>>>   -    mgr = devm_fpga_mgr_create(dev, "Altera SOCFPGA FPGA Manager",
> >>>>> -                   &socfpga_fpga_ops, priv);
> >>>>> -    if (!mgr)
> >>>>> -        return -ENOMEM;
> >>>>> +    mgr = devm_fpga_mgr_register(dev, "Altera SOCFPGA FPGA Manager",
> >>>>> +                     &socfpga_fpga_ops, priv);
> >>>>> +    if (IS_ERR(mgr))
> >>>>> +        return PTR_ERR(mgr);
> >>>>>   -    return devm_fpga_mgr_register(dev, mgr);
> >>>>> +    return 0;
> >>>>>   }
> >>>>>     #ifdef CONFIG_OF
> >>>>> diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c
> >>>>> index a2cea500f7cc..9155e888a133 100644
> >>>>> --- a/drivers/fpga/stratix10-soc.c
> >>>>> +++ b/drivers/fpga/stratix10-soc.c
> >>>>> @@ -425,18 +425,11 @@ static int s10_probe(struct platform_device *pdev)
> >>>>>         init_completion(&priv->status_return_completion);
> >>>>>   -    mgr = fpga_mgr_create(dev, "Stratix10 SOC FPGA Manager",
> >>>>> -                  &s10_ops, priv);
> >>>>> -    if (!mgr) {
> >>>>> -        dev_err(dev, "unable to create FPGA manager\n");
> >>>>> -        ret = -ENOMEM;
> >>>>> -        goto probe_err;
> >>>>> -    }
> >>>>> -
> >>>>> -    ret = fpga_mgr_register(mgr);
> >>>>> -    if (ret) {
> >>>>> +    mgr = fpga_mgr_register(dev, "Stratix10 SOC FPGA Manager",
> >>>>> +                &s10_ops, priv);
> >>>>> +    if (IS_ERR(mgr)) {
> >>>>>           dev_err(dev, "unable to register FPGA manager\n");
> >>>>> -        fpga_mgr_free(mgr);
> >>>>> +        ret = PTR_ERR(mgr);
> >>>>>           goto probe_err;
> >>>>>       }
> >>>>>   @@ -454,7 +447,6 @@ static int s10_remove(struct platform_device *pdev)
> >>>>>       struct s10_priv *priv = mgr->priv;
> >>>>>         fpga_mgr_unregister(mgr);
> >>>>> -    fpga_mgr_free(mgr);
> >>>>>       stratix10_svc_free_channel(priv->chan);
> >>>>>         return 0;
> >>>>> diff --git a/drivers/fpga/ts73xx-fpga.c b/drivers/fpga/ts73xx-fpga.c
> >>>>> index 101f016c6ed8..5efbec2b9f9f 100644
> >>>>> --- a/drivers/fpga/ts73xx-fpga.c
> >>>>> +++ b/drivers/fpga/ts73xx-fpga.c
> >>>>> @@ -122,12 +122,12 @@ static int ts73xx_fpga_probe(struct platform_device *pdev)
> >>>>>       if (IS_ERR(priv->io_base))
> >>>>>           return PTR_ERR(priv->io_base);
> >>>>>   -    mgr = devm_fpga_mgr_create(kdev, "TS-73xx FPGA Manager",
> >>>>> -                   &ts73xx_fpga_ops, priv);
> >>>>> -    if (!mgr)
> >>>>> -        return -ENOMEM;
> >>>>> +    mgr = devm_fpga_mgr_register(kdev, "TS-73xx FPGA Manager",
> >>>>> +                     &ts73xx_fpga_ops, priv);
> >>>>> +    if (IS_ERR(mgr))
> >>>>> +        return PTR_ERR(mgr);
> >>>>>   -    return devm_fpga_mgr_register(kdev, mgr);
> >>>>> +    return 0;
> >>>>>   }
> >>>>>     static struct platform_driver ts73xx_fpga_driver = {
> >>>>> diff --git a/drivers/fpga/xilinx-spi.c b/drivers/fpga/xilinx-spi.c
> >>>>> index fee4d0abf6bf..efda475f7a98 100644
> >>>>> --- a/drivers/fpga/xilinx-spi.c
> >>>>> +++ b/drivers/fpga/xilinx-spi.c
> >>>>> @@ -247,13 +247,13 @@ static int xilinx_spi_probe(struct spi_device *spi)
> >>>>>           return dev_err_probe(&spi->dev, PTR_ERR(conf->done),
> >>>>>                        "Failed to get DONE gpio\n");
> >>>>>   -    mgr = devm_fpga_mgr_create(&spi->dev,
> >>>>> -                   "Xilinx Slave Serial FPGA Manager",
> >>>>> -                   &xilinx_spi_ops, conf);
> >>>>> -    if (!mgr)
> >>>>> -        return -ENOMEM;
> >>>>> +    mgr = devm_fpga_mgr_register(&spi->dev,
> >>>>> +                     "Xilinx Slave Serial FPGA Manager",
> >>>>> +                     &xilinx_spi_ops, conf);
> >>>>> +    if (IS_ERR(mgr))
> >>>>> +        return PTR_ERR(mgr);
> >>>>>   -    return devm_fpga_mgr_register(&spi->dev, mgr);
> >>>>> +    return 0;
> >>>>>   }
> >>>>>     static const struct of_device_id xlnx_spi_of_match[] = {
> >>>>> diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c
> >>>>> index 9b75bd4f93d8..426aa34c6a0d 100644
> >>>>> --- a/drivers/fpga/zynq-fpga.c
> >>>>> +++ b/drivers/fpga/zynq-fpga.c
> >>>>> @@ -609,20 +609,16 @@ static int zynq_fpga_probe(struct platform_device *pdev)
> >>>>>         clk_disable(priv->clk);
> >>>>>   -    mgr = devm_fpga_mgr_create(dev, "Xilinx Zynq FPGA Manager",
> >>>>> -                   &zynq_fpga_ops, priv);
> >>>>> -    if (!mgr)
> >>>>> -        return -ENOMEM;
> >>>>> -
> >>>>> -    platform_set_drvdata(pdev, mgr);
> >>>>> -
> >>>>> -    err = fpga_mgr_register(mgr);
> >>>>> -    if (err) {
> >>>>> +    mgr = fpga_mgr_register(dev, "Xilinx Zynq FPGA Manager",
> >>>>> +                &zynq_fpga_ops, priv);
> >>>>> +    if (IS_ERR(mgr)) {
> >>>>>           dev_err(dev, "unable to register FPGA manager\n");
> >>>>>           clk_unprepare(priv->clk);
> >>>>> -        return err;
> >>>>> +        return PTR_ERR(mgr);
> >>>>>       }
> >>>>>   +    platform_set_drvdata(pdev, mgr);
> >>>>> +
> >>>>>       return 0;
> >>>>>   }
> >>>>>   diff --git a/drivers/fpga/zynqmp-fpga.c b/drivers/fpga/zynqmp-fpga.c
> >>>>> index 125743c9797f..1a6d520985be 100644
> >>>>> --- a/drivers/fpga/zynqmp-fpga.c
> >>>>> +++ b/drivers/fpga/zynqmp-fpga.c
> >>>>> @@ -102,12 +102,12 @@ static int zynqmp_fpga_probe(struct platform_device *pdev)
> >>>>>         priv->dev = dev;
> >>>>>   -    mgr = devm_fpga_mgr_create(dev, "Xilinx ZynqMP FPGA Manager",
> >>>>> -                   &zynqmp_fpga_ops, priv);
> >>>>> -    if (!mgr)
> >>>>> -        return -ENOMEM;
> >>>>> +    mgr = devm_fpga_mgr_register(dev, "Xilinx ZynqMP FPGA Manager",
> >>>>> +                     &zynqmp_fpga_ops, priv);
> >>>>> +    if (IS_ERR(mgr))
> >>>>> +        return PTR_ERR(mgr);
> >>>>>   -    return devm_fpga_mgr_register(dev, mgr);
> >>>>> +    return 0;
> >>>>>   }
> >>>>>     static const struct of_device_id zynqmp_fpga_of_match[] = {
> >>>>> diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
> >>>>> index 474c1f506307..d3c6f0153457 100644
> >>>>> --- a/include/linux/fpga/fpga-mgr.h
> >>>>> +++ b/include/linux/fpga/fpga-mgr.h
> >>>>> @@ -105,6 +105,17 @@ struct fpga_image_info {
> >>>>>   #endif
> >>>>>   };
> >>>>>   +/**
> >>>>> + * struct fpga_compat_id - id for compatibility check
> >>>>> + *
> >>>>> + * @id_h: high 64bit of the compat_id
> >>>>> + * @id_l: low 64bit of the compat_id
> >>>>> + */
> >>>>> +struct fpga_compat_id {
> >>>>> +    u64 id_h;
> >>>>> +    u64 id_l;
> >>>>> +};
> >>>>> +
> >>>>>   /**
> >>>>>    * struct fpga_manager_ops - ops for low level fpga manager drivers
> >>>>>    * @initial_header_size: Maximum number of bytes that should be passed into write_init
> >>>>> @@ -116,6 +127,7 @@ struct fpga_image_info {
> >>>>>    * @write_complete: set FPGA to operating state after writing is done
> >>>>>    * @fpga_remove: optional: Set FPGA into a specific state during driver remove
> >>>>>    * @groups: optional attribute groups.
> >>>>> + * @compat_id: optional: FPGA manager id for compatibility check.
> >>>>>    *
> >>>>>    * fpga_manager_ops are the low level functions implemented by a specific
> >>>>>    * fpga manager driver.  The optional ones are tested for NULL before being
> >>>>> @@ -134,6 +146,7 @@ struct fpga_manager_ops {
> >>>>>                     struct fpga_image_info *info);
> >>>>>       void (*fpga_remove)(struct fpga_manager *mgr);
> >>>>>       const struct attribute_group **groups;
> >>>>> +    struct fpga_compat_id *compat_id;
> >>>> We don't have to move compat_id here, keep it in struct fpga_mgr
> >>>>
> >>>> Thanks,
> >>>> Yilun
> >>>>
> >>>>>   };
> >>>>>     /* FPGA manager status: Partial/Full Reconfiguration errors */
> >>>>> @@ -143,24 +156,12 @@ struct fpga_manager_ops {
> >>>>>   #define FPGA_MGR_STATUS_IP_PROTOCOL_ERR        BIT(3)
> >>>>>   #define FPGA_MGR_STATUS_FIFO_OVERFLOW_ERR    BIT(4)
> >>>>>   -/**
> >>>>> - * struct fpga_compat_id - id for compatibility check
> >>>>> - *
> >>>>> - * @id_h: high 64bit of the compat_id
> >>>>> - * @id_l: low 64bit of the compat_id
> >>>>> - */
> >>>>> -struct fpga_compat_id {
> >>>>> -    u64 id_h;
> >>>>> -    u64 id_l;
> >>>>> -};
> >>>>> -
> >>>>>   /**
> >>>>>    * struct fpga_manager - fpga manager structure
> >>>>>    * @name: name of low level fpga manager
> >>>>>    * @dev: fpga manager device
> >>>>>    * @ref_mutex: only allows one reference to fpga manager
> >>>>>    * @state: state of fpga manager
> >>>>> - * @compat_id: FPGA manager id for compatibility check.
> >>>>>    * @mops: pointer to struct of fpga manager ops
> >>>>>    * @priv: low level driver private date
> >>>>>    */
> >>>>> @@ -169,7 +170,6 @@ struct fpga_manager {
> >>>>>       struct device dev;
> >>>>>       struct mutex ref_mutex;
> >>>>>       enum fpga_mgr_states state;
> >>>>> -    struct fpga_compat_id *compat_id;
> >>>>>       const struct fpga_manager_ops *mops;
> >>>>>       void *priv;
> >>>>>   };
> >>>>> @@ -191,17 +191,14 @@ struct fpga_manager *fpga_mgr_get(struct device *dev);
> >>>>>     void fpga_mgr_put(struct fpga_manager *mgr);
> >>>>>   -struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
> >>>>> -                     const struct fpga_manager_ops *mops,
> >>>>> -                     void *priv);
> >>>>> -void fpga_mgr_free(struct fpga_manager *mgr);
> >>>>> -int fpga_mgr_register(struct fpga_manager *mgr);
> >>>>> +struct fpga_manager *fpga_mgr_register(struct device *dev, const char *name,
> >>>>> +                       const struct fpga_manager_ops *mops,
> >>>>> +                       void *priv);
> >>>>>   void fpga_mgr_unregister(struct fpga_manager *mgr);
> >>>>>   -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr);
> >>>>> -
> >>>>> -struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name,
> >>>>> -                      const struct fpga_manager_ops *mops,
> >>>>> -                      void *priv);
> >>>>> +struct fpga_manager *
> >>>>> +devm_fpga_mgr_register(struct device *dev, const char *name,
> >>>>> +               const struct fpga_manager_ops *mops,
> >>>>> +               void *priv);
> >>>>>     #endif /*_LINUX_FPGA_MGR_H */
> >>>>> -- 
> >>>>> 2.25.1
> >

^ permalink raw reply	[flat|nested] 19+ messages in thread

* RE: [PATCH v5 1/3] fpga: mgr: Use standard dev_release for class driver
  2021-06-18 17:58       ` Russ Weight
  2021-06-18 20:39         ` Tom Rix
@ 2021-06-21  6:47         ` Wu, Hao
  2021-06-21  7:36         ` Xu Yilun
  2 siblings, 0 replies; 19+ messages in thread
From: Wu, Hao @ 2021-06-21  6:47 UTC (permalink / raw)
  To: Weight, Russell H, Xu, Yilun
  Cc: mdf, linux-fpga, trix, lgoncalv, Gerlach, Matthew, Gong, Richard

> On 6/18/21 9:03 AM, Russ Weight wrote:
> >
> > On 6/18/21 8:45 AM, Xu Yilun wrote:
> >> On Wed, Jun 16, 2021 at 03:57:38PM -0700, Russ Weight wrote:
> >>> The FPGA manager class driver data structure is being treated as a
> >>> managed resource instead of using standard dev_release call-back
> >>> to release the class data structure. This change removes the
> >>> managed resource code for the freeing of the class data structure
> >>> and combines the create() and register() functions into a single
> >>> register() function.
> >>>
> >>> The devm_fpga_mgr_register() function is retained.
> >>>
> >>> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
> >>> ---
> >>> v5:
> >>>   - Rebased on top of recently accepted patches.
> >>>   - Removed compat_id from the fpga_mgr_register() parameter list
> >>>     and added it to the fpga_manager_ops structure. This also required
> >> My reason for this change is, we could avoid further change of the
> >> fpga_mgr_register() API if we add new input parameters later on.
> > With this patchset, changes are only required for the callers
> > that use the new parameters.
> >
> >>>     dynamically allocating the dfl-fme-ops structure in order to add
> >>>     the appropriate compat_id.
> >> But enforcing the dynamical allocation of the parameters is not prefered
> >> to me. How about a dedicated structure that wraps all the needed
> >> parameters:
> >>
> >> struct fpga_mgr_info {
> >> 	const char *name;
> >> 	const struct fpga_manager_ops *mops;
> >> 	const struct fpga_compat_id *compat_id;
> >> 	const void *priv;
> >> };
> >>
> >> Then We can simply define a local variable of this struct for
> >> fpga_mgr_register().
> >>
> >> more details inline.
> > I agree the at the dynamic allocation is not preferred, but it is only
> > required if compat_id is used. In all other cases, the static structure
> > can continue to be used. In otherwords, only one caller is affected.
> >>
> >>> v4:
> >>>   - Added the compat_id parameter to fpga_mgr_register() and
> >>>     devm_fpga_mgr_register() to ensure that the compat_id is set before
> >>>     the device_register() call.
> >>> v3:
> >>>   - Cleaned up comment header for fpga_mgr_register()
> >>>   - Fix error return on ida_simple_get() failure
> >>> v2:
> >>>   - Restored devm_fpga_mgr_register() functionality, adapted for the
> combined
> >>>     create/register functionality.
> >>>   - All previous callers of devm_fpga_mgr_register() will continue to call
> >>>     devm_fpga_mgr_register().
> >>>   - replaced unnecessary ternary operators in return statements with
> standard
> >>>     if conditions.
> >>> ---
> >>>  drivers/fpga/altera-cvp.c        |  12 +--
> >>>  drivers/fpga/altera-pr-ip-core.c |   8 +-
> >>>  drivers/fpga/altera-ps-spi.c     |  10 +-
> >>>  drivers/fpga/dfl-fme-mgr.c       |  52 ++++++----
> >>>  drivers/fpga/dfl-fme-region.c    |   2 +-
> >>>  drivers/fpga/fpga-mgr.c          | 163 ++++++++-----------------------
> >>>  drivers/fpga/ice40-spi.c         |  10 +-
> >>>  drivers/fpga/machxo2-spi.c       |  10 +-
> >>>  drivers/fpga/socfpga-a10.c       |  16 ++-
> >>>  drivers/fpga/socfpga.c           |  10 +-
> >>>  drivers/fpga/stratix10-soc.c     |  16 +--
> >>>  drivers/fpga/ts73xx-fpga.c       |  10 +-
> >>>  drivers/fpga/xilinx-spi.c        |  12 +--
> >>>  drivers/fpga/zynq-fpga.c         |  16 ++-
> >>>  drivers/fpga/zynqmp-fpga.c       |  10 +-
> >>>  include/linux/fpga/fpga-mgr.h    |  43 ++++----
> >>>  16 files changed, 153 insertions(+), 247 deletions(-)
> >>>
> >>> diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
> >>> index ccf4546eff29..4ffb9da537d8 100644
> >>> --- a/drivers/fpga/altera-cvp.c
> >>> +++ b/drivers/fpga/altera-cvp.c
> >>> @@ -652,19 +652,15 @@ static int altera_cvp_probe(struct pci_dev *pdev,
> >>>  	snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s @%s",
> >>>  		 ALTERA_CVP_MGR_NAME, pci_name(pdev));
> >>>
> >>> -	mgr = devm_fpga_mgr_create(&pdev->dev, conf->mgr_name,
> >>> -				   &altera_cvp_ops, conf);
> >>> -	if (!mgr) {
> >>> -		ret = -ENOMEM;
> >>> +	mgr = fpga_mgr_register(&pdev->dev, conf->mgr_name,
> >>> +				&altera_cvp_ops, conf);
> >>> +	if (IS_ERR(mgr)) {
> >>> +		ret = PTR_ERR(mgr);
> >>>  		goto err_unmap;
> >>>  	}
> >>>
> >>>  	pci_set_drvdata(pdev, mgr);
> >>>
> >>> -	ret = fpga_mgr_register(mgr);
> >>> -	if (ret)
> >>> -		goto err_unmap;
> >>> -
> >>>  	return 0;
> >>>
> >>>  err_unmap:
> >>> diff --git a/drivers/fpga/altera-pr-ip-core.c b/drivers/fpga/altera-pr-ip-
> core.c
> >>> index dfdf21ed34c4..17babf974852 100644
> >>> --- a/drivers/fpga/altera-pr-ip-core.c
> >>> +++ b/drivers/fpga/altera-pr-ip-core.c
> >>> @@ -191,11 +191,11 @@ int alt_pr_register(struct device *dev, void
> __iomem *reg_base)
> >>>  		(val & ALT_PR_CSR_STATUS_MSK) >> ALT_PR_CSR_STATUS_SFT,
> >>>  		(int)(val & ALT_PR_CSR_PR_START));
> >>>
> >>> -	mgr = devm_fpga_mgr_create(dev, dev_name(dev), &alt_pr_ops, priv);
> >>> -	if (!mgr)
> >>> -		return -ENOMEM;
> >>> +	mgr = devm_fpga_mgr_register(dev, dev_name(dev), &alt_pr_ops, priv);
> >>> +	if (IS_ERR(mgr))
> >>> +		return PTR_ERR(mgr);
> >>>
> >>> -	return devm_fpga_mgr_register(dev, mgr);
> >>> +	return 0;
> >>>  }
> >>>  EXPORT_SYMBOL_GPL(alt_pr_register);
> >>>
> >>> diff --git a/drivers/fpga/altera-ps-spi.c b/drivers/fpga/altera-ps-spi.c
> >>> index 23bfd4d1ad0f..d3f77b0312b2 100644
> >>> --- a/drivers/fpga/altera-ps-spi.c
> >>> +++ b/drivers/fpga/altera-ps-spi.c
> >>> @@ -302,12 +302,12 @@ static int altera_ps_probe(struct spi_device *spi)
> >>>  	snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s %s",
> >>>  		 dev_driver_string(&spi->dev), dev_name(&spi->dev));
> >>>
> >>> -	mgr = devm_fpga_mgr_create(&spi->dev, conf->mgr_name,
> >>> -				   &altera_ps_ops, conf);
> >>> -	if (!mgr)
> >>> -		return -ENOMEM;
> >>> +	mgr = devm_fpga_mgr_register(&spi->dev, conf->mgr_name,
> >>> +				     &altera_ps_ops, conf);
> >>> +	if (IS_ERR(mgr))
> >>> +		return PTR_ERR(mgr);
> >>>
> >>> -	return devm_fpga_mgr_register(&spi->dev, mgr);
> >>> +	return 0;
> >>>  }
> >>>
> >>>  static const struct spi_device_id altera_ps_spi_ids[] = {
> >>> diff --git a/drivers/fpga/dfl-fme-mgr.c b/drivers/fpga/dfl-fme-mgr.c
> >>> index d5861d13b306..1b93bc292dbe 100644
> >>> --- a/drivers/fpga/dfl-fme-mgr.c
> >>> +++ b/drivers/fpga/dfl-fme-mgr.c
> >>> @@ -264,14 +264,6 @@ static u64 fme_mgr_status(struct fpga_manager
> *mgr)
> >>>  	return pr_error_to_mgr_status(priv->pr_error);
> >>>  }
> >>>
> >>> -static const struct fpga_manager_ops fme_mgr_ops = {
> >>> -	.write_init = fme_mgr_write_init,
> >>> -	.write = fme_mgr_write,
> >>> -	.write_complete = fme_mgr_write_complete,
> >>> -	.state = fme_mgr_state,
> >>> -	.status = fme_mgr_status,
> >>> -};
> >>> -
> >>>  static void fme_mgr_get_compat_id(void __iomem *fme_pr,
> >>>  				  struct fpga_compat_id *id)
> >>>  {
> >>> @@ -279,10 +271,34 @@ static void fme_mgr_get_compat_id(void
> __iomem *fme_pr,
> >>>  	id->id_h = readq(fme_pr + FME_PR_INTFC_ID_H);
> >>>  }
> >>>
> >>> +static struct fpga_manager_ops *fme_mgr_get_ops(struct device *dev,
> >>> +						struct fme_mgr_priv *priv)
> >>> +{
> >>> +	struct fpga_manager_ops *ops;
> >>> +
> >>> +	ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL);
> >>> +	if (!ops)
> >>> +		return NULL;
> >>> +
> >>> +	ops->compat_id = devm_kzalloc(dev, sizeof(struct fpga_compat_id),
> >>> +				      GFP_KERNEL);
> >>> +	if (!ops->compat_id)
> >>> +		return NULL;
> >>> +
> >>> +	fme_mgr_get_compat_id(priv->ioaddr, ops->compat_id);
> >>> +	ops->write_init = fme_mgr_write_init;
> >>> +	ops->write = fme_mgr_write;
> >>> +	ops->write_complete = fme_mgr_write_complete;
> >>> +	ops->state = fme_mgr_state;
> >>> +	ops->status = fme_mgr_status;
> >>> +
> >>> +	return ops;
> >>> +}
> 
> What do other's think? Is it better to dynamically allocate the ops structure
> for users of compat_id (just one user at this time)? Or better to create an
> info structure on the stack for all callers? See above for an example of a
> dynamically allocated the ops structure.

To me, it seems not good to put compat_id into ops, ops should be provided by
driver, but compat_id is something from hardware for compatible checking, so
this is why compat_id is in fpga-mgr and fpga-region. compat_id can be fpga mgr
level or per each fpga region, this is why we have both there.

Currently we have allocation code in specific fpga mgr driver (e.g. dfl), as most
drivers are not using them at all. To me, keep the allocation code here or move
it into some common code, both are fine to me, but add this to ops seems confusing.

Thanks
Hao

> 
> To me, using the ops structure seems more standard, and the dynamic allocation,
> while not optimal, does not require much more space or complexity than the
> static
> allocation. At this time it only affects one caller.
> 
> Adding the info structure as a parameter to the register() functions adds a
> little more complexity to all callers, whether or not they use the dynamic
> elements of the structure.
> 
> - Russ
> 


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v5 1/3] fpga: mgr: Use standard dev_release for class driver
  2021-06-18 17:58       ` Russ Weight
  2021-06-18 20:39         ` Tom Rix
  2021-06-21  6:47         ` Wu, Hao
@ 2021-06-21  7:36         ` Xu Yilun
  2 siblings, 0 replies; 19+ messages in thread
From: Xu Yilun @ 2021-06-21  7:36 UTC (permalink / raw)
  To: Russ Weight
  Cc: mdf, linux-fpga, trix, lgoncalv, hao.wu, matthew.gerlach, richard.gong

On Fri, Jun 18, 2021 at 10:58:36AM -0700, Russ Weight wrote:
> 
> 
> On 6/18/21 9:03 AM, Russ Weight wrote:
> >
> > On 6/18/21 8:45 AM, Xu Yilun wrote:
> >> On Wed, Jun 16, 2021 at 03:57:38PM -0700, Russ Weight wrote:
> >>> The FPGA manager class driver data structure is being treated as a
> >>> managed resource instead of using standard dev_release call-back
> >>> to release the class data structure. This change removes the
> >>> managed resource code for the freeing of the class data structure
> >>> and combines the create() and register() functions into a single
> >>> register() function.
> >>>
> >>> The devm_fpga_mgr_register() function is retained.
> >>>
> >>> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
> >>> ---
> >>> v5:
> >>>   - Rebased on top of recently accepted patches.
> >>>   - Removed compat_id from the fpga_mgr_register() parameter list
> >>>     and added it to the fpga_manager_ops structure. This also required
> >> My reason for this change is, we could avoid further change of the
> >> fpga_mgr_register() API if we add new input parameters later on.
> > With this patchset, changes are only required for the callers
> > that use the new parameters.
> >
> >>>     dynamically allocating the dfl-fme-ops structure in order to add
> >>>     the appropriate compat_id.
> >> But enforcing the dynamical allocation of the parameters is not prefered
> >> to me. How about a dedicated structure that wraps all the needed
> >> parameters:
> >>
> >> struct fpga_mgr_info {
> >> 	const char *name;
> >> 	const struct fpga_manager_ops *mops;
> >> 	const struct fpga_compat_id *compat_id;
> >> 	const void *priv;
> >> };
> >>
> >> Then We can simply define a local variable of this struct for
> >> fpga_mgr_register().
> >>
> >> more details inline.
> > I agree the at the dynamic allocation is not preferred, but it is only
> > required if compat_id is used. In all other cases, the static structure
> > can continue to be used. In otherwords, only one caller is affected.
> >>
> >>> v4:
> >>>   - Added the compat_id parameter to fpga_mgr_register() and
> >>>     devm_fpga_mgr_register() to ensure that the compat_id is set before
> >>>     the device_register() call.
> >>> v3:
> >>>   - Cleaned up comment header for fpga_mgr_register()
> >>>   - Fix error return on ida_simple_get() failure
> >>> v2:
> >>>   - Restored devm_fpga_mgr_register() functionality, adapted for the combined
> >>>     create/register functionality.
> >>>   - All previous callers of devm_fpga_mgr_register() will continue to call
> >>>     devm_fpga_mgr_register().
> >>>   - replaced unnecessary ternary operators in return statements with standard
> >>>     if conditions.
> >>> ---
> >>>  drivers/fpga/altera-cvp.c        |  12 +--
> >>>  drivers/fpga/altera-pr-ip-core.c |   8 +-
> >>>  drivers/fpga/altera-ps-spi.c     |  10 +-
> >>>  drivers/fpga/dfl-fme-mgr.c       |  52 ++++++----
> >>>  drivers/fpga/dfl-fme-region.c    |   2 +-
> >>>  drivers/fpga/fpga-mgr.c          | 163 ++++++++-----------------------
> >>>  drivers/fpga/ice40-spi.c         |  10 +-
> >>>  drivers/fpga/machxo2-spi.c       |  10 +-
> >>>  drivers/fpga/socfpga-a10.c       |  16 ++-
> >>>  drivers/fpga/socfpga.c           |  10 +-
> >>>  drivers/fpga/stratix10-soc.c     |  16 +--
> >>>  drivers/fpga/ts73xx-fpga.c       |  10 +-
> >>>  drivers/fpga/xilinx-spi.c        |  12 +--
> >>>  drivers/fpga/zynq-fpga.c         |  16 ++-
> >>>  drivers/fpga/zynqmp-fpga.c       |  10 +-
> >>>  include/linux/fpga/fpga-mgr.h    |  43 ++++----
> >>>  16 files changed, 153 insertions(+), 247 deletions(-)
> >>>
> >>> diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
> >>> index ccf4546eff29..4ffb9da537d8 100644
> >>> --- a/drivers/fpga/altera-cvp.c
> >>> +++ b/drivers/fpga/altera-cvp.c
> >>> @@ -652,19 +652,15 @@ static int altera_cvp_probe(struct pci_dev *pdev,
> >>>  	snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s @%s",
> >>>  		 ALTERA_CVP_MGR_NAME, pci_name(pdev));
> >>>  
> >>> -	mgr = devm_fpga_mgr_create(&pdev->dev, conf->mgr_name,
> >>> -				   &altera_cvp_ops, conf);
> >>> -	if (!mgr) {
> >>> -		ret = -ENOMEM;
> >>> +	mgr = fpga_mgr_register(&pdev->dev, conf->mgr_name,
> >>> +				&altera_cvp_ops, conf);
> >>> +	if (IS_ERR(mgr)) {
> >>> +		ret = PTR_ERR(mgr);
> >>>  		goto err_unmap;
> >>>  	}
> >>>  
> >>>  	pci_set_drvdata(pdev, mgr);
> >>>  
> >>> -	ret = fpga_mgr_register(mgr);
> >>> -	if (ret)
> >>> -		goto err_unmap;
> >>> -
> >>>  	return 0;
> >>>  
> >>>  err_unmap:
> >>> diff --git a/drivers/fpga/altera-pr-ip-core.c b/drivers/fpga/altera-pr-ip-core.c
> >>> index dfdf21ed34c4..17babf974852 100644
> >>> --- a/drivers/fpga/altera-pr-ip-core.c
> >>> +++ b/drivers/fpga/altera-pr-ip-core.c
> >>> @@ -191,11 +191,11 @@ int alt_pr_register(struct device *dev, void __iomem *reg_base)
> >>>  		(val & ALT_PR_CSR_STATUS_MSK) >> ALT_PR_CSR_STATUS_SFT,
> >>>  		(int)(val & ALT_PR_CSR_PR_START));
> >>>  
> >>> -	mgr = devm_fpga_mgr_create(dev, dev_name(dev), &alt_pr_ops, priv);
> >>> -	if (!mgr)
> >>> -		return -ENOMEM;
> >>> +	mgr = devm_fpga_mgr_register(dev, dev_name(dev), &alt_pr_ops, priv);
> >>> +	if (IS_ERR(mgr))
> >>> +		return PTR_ERR(mgr);
> >>>  
> >>> -	return devm_fpga_mgr_register(dev, mgr);
> >>> +	return 0;
> >>>  }
> >>>  EXPORT_SYMBOL_GPL(alt_pr_register);
> >>>  
> >>> diff --git a/drivers/fpga/altera-ps-spi.c b/drivers/fpga/altera-ps-spi.c
> >>> index 23bfd4d1ad0f..d3f77b0312b2 100644
> >>> --- a/drivers/fpga/altera-ps-spi.c
> >>> +++ b/drivers/fpga/altera-ps-spi.c
> >>> @@ -302,12 +302,12 @@ static int altera_ps_probe(struct spi_device *spi)
> >>>  	snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s %s",
> >>>  		 dev_driver_string(&spi->dev), dev_name(&spi->dev));
> >>>  
> >>> -	mgr = devm_fpga_mgr_create(&spi->dev, conf->mgr_name,
> >>> -				   &altera_ps_ops, conf);
> >>> -	if (!mgr)
> >>> -		return -ENOMEM;
> >>> +	mgr = devm_fpga_mgr_register(&spi->dev, conf->mgr_name,
> >>> +				     &altera_ps_ops, conf);
> >>> +	if (IS_ERR(mgr))
> >>> +		return PTR_ERR(mgr);
> >>>  
> >>> -	return devm_fpga_mgr_register(&spi->dev, mgr);
> >>> +	return 0;
> >>>  }
> >>>  
> >>>  static const struct spi_device_id altera_ps_spi_ids[] = {
> >>> diff --git a/drivers/fpga/dfl-fme-mgr.c b/drivers/fpga/dfl-fme-mgr.c
> >>> index d5861d13b306..1b93bc292dbe 100644
> >>> --- a/drivers/fpga/dfl-fme-mgr.c
> >>> +++ b/drivers/fpga/dfl-fme-mgr.c
> >>> @@ -264,14 +264,6 @@ static u64 fme_mgr_status(struct fpga_manager *mgr)
> >>>  	return pr_error_to_mgr_status(priv->pr_error);
> >>>  }
> >>>  
> >>> -static const struct fpga_manager_ops fme_mgr_ops = {
> >>> -	.write_init = fme_mgr_write_init,
> >>> -	.write = fme_mgr_write,
> >>> -	.write_complete = fme_mgr_write_complete,
> >>> -	.state = fme_mgr_state,
> >>> -	.status = fme_mgr_status,
> >>> -};
> >>> -
> >>>  static void fme_mgr_get_compat_id(void __iomem *fme_pr,
> >>>  				  struct fpga_compat_id *id)
> >>>  {
> >>> @@ -279,10 +271,34 @@ static void fme_mgr_get_compat_id(void __iomem *fme_pr,
> >>>  	id->id_h = readq(fme_pr + FME_PR_INTFC_ID_H);
> >>>  }
> >>>  
> >>> +static struct fpga_manager_ops *fme_mgr_get_ops(struct device *dev,
> >>> +						struct fme_mgr_priv *priv)
> >>> +{
> >>> +	struct fpga_manager_ops *ops;
> >>> +
> >>> +	ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL);
> >>> +	if (!ops)
> >>> +		return NULL;
> >>> +
> >>> +	ops->compat_id = devm_kzalloc(dev, sizeof(struct fpga_compat_id),
> >>> +				      GFP_KERNEL);
> >>> +	if (!ops->compat_id)
> >>> +		return NULL;
> >>> +
> >>> +	fme_mgr_get_compat_id(priv->ioaddr, ops->compat_id);
> >>> +	ops->write_init = fme_mgr_write_init;
> >>> +	ops->write = fme_mgr_write;
> >>> +	ops->write_complete = fme_mgr_write_complete;
> >>> +	ops->state = fme_mgr_state;
> >>> +	ops->status = fme_mgr_status;
> >>> +
> >>> +	return ops;
> >>> +}
> 
> What do other's think? Is it better to dynamically allocate the ops structure
> for users of compat_id (just one user at this time)? Or better to create an
> info structure on the stack for all callers? See above for an example of a
> dynamically allocated the ops structure.
> 
> To me, using the ops structure seems more standard, and the dynamic allocation,
> while not optimal, does not require much more space or complexity than the static
> allocation. At this time it only affects one caller.
> 
> Adding the info structure as a parameter to the register() functions adds a
> little more complexity to all callers, whether or not they use the dynamic
> elements of the structure.

So could we keep the definition:

struct fpga_manager *fpga_mgr_register(struct device *dev, const char *name,
				       const struct fpga_manager_ops *mops,
				       void *priv)

while have another APIs for those with extra parameters:

struct fpga_manager *fpga_mgr_register_with_info(struct device *dev,
						 struct fpga_manager_info)


My intent of the info structure is to stablize the fpga-mgr/region APIs.
We are changing the API definition and will have to touch all the fpga
device drivers this time. It may be better we don't do it again.

When we are creating the fpga_mgr class dev, we may have some device
specific values filled to the fpga_mgr instance.
And previously with fpga_mgr_create & fpga_mgr_register, in device drivers
we can:

	mgr = fpga_mgr_create();

	/* device specific init */
	mgr->field_a = ...
	mgr->field_b = ...
	...
	
	fpga_mgr_register(mgr);

Now, we decide to have a single fpga_mgr_register(), so drivers should pass
all device specific values on fpga_mgr_register().

One solution is we add more input parameters for fpga_mgr_register(), and
touch all callers. Next time we add another input parameters, we touch
them again.

So if we have the info structure that wraps all parameters, we just
change the info structure next time.

Thanks
Yilun

> 
> - Russ
> 
> >>> +
> >>>  static int fme_mgr_probe(struct platform_device *pdev)
> >>>  {
> >>>  	struct dfl_fme_mgr_pdata *pdata = dev_get_platdata(&pdev->dev);
> >>> -	struct fpga_compat_id *compat_id;
> >>> +	struct fpga_manager_ops *fme_mgr_ops;
> >>>  	struct device *dev = &pdev->dev;
> >>>  	struct fme_mgr_priv *priv;
> >>>  	struct fpga_manager *mgr;
> >>> @@ -302,20 +318,16 @@ static int fme_mgr_probe(struct platform_device *pdev)
> >>>  			return PTR_ERR(priv->ioaddr);
> >>>  	}
> >>>  
> >>> -	compat_id = devm_kzalloc(dev, sizeof(*compat_id), GFP_KERNEL);
> >>> -	if (!compat_id)
> >>> -		return -ENOMEM;
> >>> -
> >>> -	fme_mgr_get_compat_id(priv->ioaddr, compat_id);
> >>> -
> >>> -	mgr = devm_fpga_mgr_create(dev, "DFL FME FPGA Manager",
> >>> -				   &fme_mgr_ops, priv);
> >>> -	if (!mgr)
> >>> +	fme_mgr_ops = fme_mgr_get_ops(dev, priv);
> >>> +	if (!fme_mgr_ops)
> >>>  		return -ENOMEM;
> >>>  
> >>> -	mgr->compat_id = compat_id;
> >>> +	mgr = devm_fpga_mgr_register(dev, "DFL FME FPGA Manager",
> >>> +				     fme_mgr_ops, priv);
> >>> +	if (IS_ERR(mgr))
> >>> +		return PTR_ERR(mgr);
> >> With structure fpga_mgr_info, we don't need the dynamical allocation
> >> anymore:
> >>
> >> 	struct fpga_mgr_info minfo = { 0 };
> >>
> >> 	minfo.name = "DFL FME FPGA Manager";
> >> 	minfo.mops = &fme_mgr_ops;
> >> 	minfo.compat_id = compat_id;
> >> 	minfo.priv = priv;
> >>
> >> 	mgr = devm_fpga_mgr_register(dev, &minfo);
> >>
> >> Drivers could also leaves minfo.compat_id zero if they don't care.
> >>
> >> I also expect more opinions.
> >>
> >>>  
> >>> -	return devm_fpga_mgr_register(dev, mgr);
> >>> +	return 0;
> >>>  }
> >>>  
> >>>  static struct platform_driver fme_mgr_driver = {
> >>> diff --git a/drivers/fpga/dfl-fme-region.c b/drivers/fpga/dfl-fme-region.c
> >>> index 1eeb42af1012..a64c13e198a0 100644
> >>> --- a/drivers/fpga/dfl-fme-region.c
> >>> +++ b/drivers/fpga/dfl-fme-region.c
> >>> @@ -46,7 +46,7 @@ static int fme_region_probe(struct platform_device *pdev)
> >>>  	}
> >>>  
> >>>  	region->priv = pdata;
> >>> -	region->compat_id = mgr->compat_id;
> >>> +	region->compat_id = mgr->mops->compat_id;
> >> The changes should be in Patch #3, is it?
> > This is the patch that moves compat_id out of mgr and into mgr->mops.
> > In order to be able to compile, this patch needs the change.
> >
> >>>  	platform_set_drvdata(pdev, region);
> >>>  
> >>>  	ret = fpga_region_register(region);
> >>> diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
> >>> index ecb4c3c795fa..9aecfea432c8 100644
> >>> --- a/drivers/fpga/fpga-mgr.c
> >>> +++ b/drivers/fpga/fpga-mgr.c
> >>> @@ -550,20 +550,20 @@ void fpga_mgr_unlock(struct fpga_manager *mgr)
> >>>  EXPORT_SYMBOL_GPL(fpga_mgr_unlock);
> >>>  
> >>>  /**
> >>> - * fpga_mgr_create - create and initialize an FPGA manager struct
> >>> + * 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
> >>>   *
> >>> - * 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 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 *parent, 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)
> >> fpga_mgr_register(struct device *parent, struct fpga_mgr_info *minfo)
> >>
> >>>  {
> >>>  	struct fpga_manager *mgr;
> >>>  	int id, ret;
> >>> @@ -572,21 +572,23 @@ struct fpga_manager *fpga_mgr_create(struct device *parent, const char *name,
> >>>  	    !mops->write_init || (!mops->write && !mops->write_sg) ||
> >>>  	    (mops->write && mops->write_sg)) {
> >>>  		dev_err(parent, "Attempt to register without fpga_manager_ops\n");
> >>> -		return NULL;
> >>> +		return ERR_PTR(-EINVAL);
> >>>  	}
> >>>  
> >>>  	if (!name || !strlen(name)) {
> >>>  		dev_err(parent, "Attempt to register with no name!\n");
> >>> -		return NULL;
> >>> +		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)
> >>> +	if (id < 0) {
> >>> +		ret = id;
> >>>  		goto error_kfree;
> >>> +	}
> >>>  
> >>>  	mutex_init(&mgr->ref_mutex);
> >>>  
> >>> @@ -594,7 +596,6 @@ struct fpga_manager *fpga_mgr_create(struct device *parent, const char *name,
> >>>  	mgr->mops = mops;
> >>>  	mgr->priv = priv;
> >> 	mgr->name = minfo->name;
> >> 	mgr->mops = minfo->mops;
> >> 	mgr->priv = minfo->priv;
> >> 	mgr->compat_id = minfo->compat_id;
> >>
> >>>  
> >>> -	device_initialize(&mgr->dev);
> >>>  	mgr->dev.class = fpga_mgr_class;
> >>>  	mgr->dev.groups = mops->groups;
> >>>  	mgr->dev.parent = parent;
> >>> @@ -605,84 +606,6 @@ struct fpga_manager *fpga_mgr_create(struct device *parent, const char *name,
> >>>  	if (ret)
> >>>  		goto error_device;
> >>>  
> >>> -	return mgr;
> >>> -
> >>> -error_device:
> >>> -	ida_simple_remove(&fpga_mgr_ida, id);
> >>> -error_kfree:
> >>> -	kfree(mgr);
> >>> -
> >>> -	return NULL;
> >>> -}
> >>> -EXPORT_SYMBOL_GPL(fpga_mgr_create);
> >>> -
> >>> -/**
> >>> - * fpga_mgr_free - free an 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_mgr_devres *dr = res;
> >>> -
> >>> -	fpga_mgr_free(dr->mgr);
> >>> -}
> >>> -
> >>> -/**
> >>> - * devm_fpga_mgr_create - create and initialize a managed FPGA manager struct
> >>> - * @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 an 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.
> >>> - *
> >>> - * Return: pointer to struct fpga_manager or NULL
> >>> - */
> >>> -struct fpga_manager *devm_fpga_mgr_create(struct device *parent, const char *name,
> >>> -					  const struct fpga_manager_ops *mops,
> >>> -					  void *priv)
> >>> -{
> >>> -	struct fpga_mgr_devres *dr;
> >>> -
> >>> -	dr = devres_alloc(devm_fpga_mgr_release, sizeof(*dr), GFP_KERNEL);
> >>> -	if (!dr)
> >>> -		return NULL;
> >>> -
> >>> -	dr->mgr = fpga_mgr_create(parent, name, mops, priv);
> >>> -	if (!dr->mgr) {
> >>> -		devres_free(dr);
> >>> -		return NULL;
> >>> -	}
> >>> -
> >>> -	devres_add(parent, dr);
> >>> -
> >>> -	return dr->mgr;
> >>> -}
> >>> -EXPORT_SYMBOL_GPL(devm_fpga_mgr_create);
> >>> -
> >>> -/**
> >>> - * fpga_mgr_register - register an FPGA manager
> >>> - * @mgr: fpga manager struct
> >>> - *
> >>> - * Return: 0 on success, negative error code otherwise.
> >>> - */
> >>> -int fpga_mgr_register(struct fpga_manager *mgr)
> >>> -{
> >>> -	int ret;
> >>> -
> >>>  	/*
> >>>  	 * Initialize framework state by requesting low level driver read state
> >>>  	 * from device.  FPGA may be in reset mode or may have been programmed
> >>> @@ -690,18 +613,20 @@ int fpga_mgr_register(struct fpga_manager *mgr)
> >>>  	 */
> >>>  	mgr->state = mgr->mops->state(mgr);
> >>>  
> >>> -	ret = device_add(&mgr->dev);
> >>> -	if (ret)
> >>> -		goto error_device;
> >>> -
> >>> -	dev_info(&mgr->dev, "%s registered\n", mgr->name);
> >>> +	ret = device_register(&mgr->dev);
> >>> +	if (ret) {
> >>> +		put_device(&mgr->dev);
> >>> +		return ERR_PTR(ret);
> >>> +	}
> >>>  
> >>> -	return 0;
> >>> +	return mgr;
> >>>  
> >>>  error_device:
> >>> -	ida_simple_remove(&fpga_mgr_ida, mgr->dev.id);
> >>> +	ida_simple_remove(&fpga_mgr_ida, id);
> >>> +error_kfree:
> >>> +	kfree(mgr);
> >>>  
> >>> -	return ret;
> >>> +	return ERR_PTR(ret);
> >>>  }
> >>>  EXPORT_SYMBOL_GPL(fpga_mgr_register);
> >>>  
> >>> @@ -726,14 +651,6 @@ void fpga_mgr_unregister(struct fpga_manager *mgr)
> >>>  }
> >>>  EXPORT_SYMBOL_GPL(fpga_mgr_unregister);
> >>>  
> >>> -static int fpga_mgr_devres_match(struct device *dev, void *res,
> >>> -				 void *match_data)
> >>> -{
> >>> -	struct fpga_mgr_devres *dr = res;
> >>> -
> >>> -	return match_data == dr->mgr;
> >>> -}
> >>> -
> >>>  static void devm_fpga_mgr_unregister(struct device *dev, void *res)
> >>>  {
> >>>  	struct fpga_mgr_devres *dr = res;
> >>> @@ -743,44 +660,44 @@ static void devm_fpga_mgr_unregister(struct device *dev, void *res)
> >>>  
> >>>  /**
> >>>   * devm_fpga_mgr_register - resource managed variant of fpga_mgr_register()
> >>> - * @dev: managing device for this FPGA manager
> >>> - * @mgr: 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
> >>>   *
> >>>   * This is the devres variant of fpga_mgr_register() for which the unregister
> >>>   * function will be called automatically when the managing device is detached.
> >>>   */
> >>> -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr)
> >>> +struct fpga_manager *
> >>> +devm_fpga_mgr_register(struct device *dev, const char *name,
> >>    devm_fpga_mgr_register(struct device *parent,
> > Yes - thanks for catching that.
> >
> > - Russ
> >
> >>
> >>> +		       const struct fpga_manager_ops *mops, void *priv)
> >>>  {
> >>>  	struct fpga_mgr_devres *dr;
> >>> -	int ret;
> >>> -
> >>> -	/*
> >>> -	 * Make sure that the struct fpga_manager * that is passed in is
> >>> -	 * managed itself.
> >>> -	 */
> >>> -	if (WARN_ON(!devres_find(dev, devm_fpga_mgr_release,
> >>> -				 fpga_mgr_devres_match, mgr)))
> >>> -		return -EINVAL;
> >>> +	struct fpga_manager *mgr;
> >>>  
> >>>  	dr = devres_alloc(devm_fpga_mgr_unregister, sizeof(*dr), GFP_KERNEL);
> >>>  	if (!dr)
> >>> -		return -ENOMEM;
> >>> +		return ERR_PTR(-ENOMEM);
> >>>  
> >>> -	ret = fpga_mgr_register(mgr);
> >>> -	if (ret) {
> >>> +	mgr = fpga_mgr_register(dev, name, mops, priv);
> >>> +	if (IS_ERR(mgr)) {
> >>>  		devres_free(dr);
> >>> -		return ret;
> >>> +		return mgr;
> >>>  	}
> >>>  
> >>>  	dr->mgr = mgr;
> >>>  	devres_add(dev, dr);
> >>>  
> >>> -	return 0;
> >>> +	return mgr;
> >>>  }
> >>>  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)
> >>> diff --git a/drivers/fpga/ice40-spi.c b/drivers/fpga/ice40-spi.c
> >>> index 69dec5af23c3..91ac25f339f3 100644
> >>> --- a/drivers/fpga/ice40-spi.c
> >>> +++ b/drivers/fpga/ice40-spi.c
> >>> @@ -178,12 +178,12 @@ static int ice40_fpga_probe(struct spi_device *spi)
> >>>  		return ret;
> >>>  	}
> >>>  
> >>> -	mgr = devm_fpga_mgr_create(dev, "Lattice iCE40 FPGA Manager",
> >>> -				   &ice40_fpga_ops, priv);
> >>> -	if (!mgr)
> >>> -		return -ENOMEM;
> >>> +	mgr = devm_fpga_mgr_register(dev, "Lattice iCE40 FPGA Manager",
> >>> +				     &ice40_fpga_ops, priv);
> >>> +	if (IS_ERR(mgr))
> >>> +		return PTR_ERR(mgr);
> >>>  
> >>> -	return devm_fpga_mgr_register(dev, mgr);
> >>> +	return 0;
> >>>  }
> >>>  
> >>>  static const struct of_device_id ice40_fpga_of_match[] = {
> >>> diff --git a/drivers/fpga/machxo2-spi.c b/drivers/fpga/machxo2-spi.c
> >>> index 114a64d2b7a4..0137c1e2e706 100644
> >>> --- a/drivers/fpga/machxo2-spi.c
> >>> +++ b/drivers/fpga/machxo2-spi.c
> >>> @@ -366,12 +366,12 @@ static int machxo2_spi_probe(struct spi_device *spi)
> >>>  		return -EINVAL;
> >>>  	}
> >>>  
> >>> -	mgr = devm_fpga_mgr_create(dev, "Lattice MachXO2 SPI FPGA Manager",
> >>> -				   &machxo2_ops, spi);
> >>> -	if (!mgr)
> >>> -		return -ENOMEM;
> >>> +	mgr = devm_fpga_mgr_register(dev, "Lattice MachXO2 SPI FPGA Manager",
> >>> +				     &machxo2_ops, spi);
> >>> +	if (IS_ERR(mgr))
> >>> +		return PTR_ERR(mgr);
> >>>  
> >>> -	return devm_fpga_mgr_register(dev, mgr);
> >>> +	return 0;
> >>>  }
> >>>  
> >>>  static const struct of_device_id of_match[] = {
> >>> diff --git a/drivers/fpga/socfpga-a10.c b/drivers/fpga/socfpga-a10.c
> >>> index 573d88bdf730..ac8e89b8a5cc 100644
> >>> --- a/drivers/fpga/socfpga-a10.c
> >>> +++ b/drivers/fpga/socfpga-a10.c
> >>> @@ -508,19 +508,15 @@ static int socfpga_a10_fpga_probe(struct platform_device *pdev)
> >>>  		return -EBUSY;
> >>>  	}
> >>>  
> >>> -	mgr = devm_fpga_mgr_create(dev, "SoCFPGA Arria10 FPGA Manager",
> >>> -				   &socfpga_a10_fpga_mgr_ops, priv);
> >>> -	if (!mgr)
> >>> -		return -ENOMEM;
> >>> -
> >>> -	platform_set_drvdata(pdev, mgr);
> >>> -
> >>> -	ret = fpga_mgr_register(mgr);
> >>> -	if (ret) {
> >>> +	mgr = fpga_mgr_register(dev, "SoCFPGA Arria10 FPGA Manager",
> >>> +				&socfpga_a10_fpga_mgr_ops, priv);
> >>> +	if (IS_ERR(mgr)) {
> >>>  		clk_disable_unprepare(priv->clk);
> >>> -		return ret;
> >>> +		return PTR_ERR(mgr);
> >>>  	}
> >>>  
> >>> +	platform_set_drvdata(pdev, mgr);
> >>> +
> >>>  	return 0;
> >>>  }
> >>>  
> >>> diff --git a/drivers/fpga/socfpga.c b/drivers/fpga/socfpga.c
> >>> index 1f467173fc1f..ebfa9f4d24ef 100644
> >>> --- a/drivers/fpga/socfpga.c
> >>> +++ b/drivers/fpga/socfpga.c
> >>> @@ -571,12 +571,12 @@ static int socfpga_fpga_probe(struct platform_device *pdev)
> >>>  	if (ret)
> >>>  		return ret;
> >>>  
> >>> -	mgr = devm_fpga_mgr_create(dev, "Altera SOCFPGA FPGA Manager",
> >>> -				   &socfpga_fpga_ops, priv);
> >>> -	if (!mgr)
> >>> -		return -ENOMEM;
> >>> +	mgr = devm_fpga_mgr_register(dev, "Altera SOCFPGA FPGA Manager",
> >>> +				     &socfpga_fpga_ops, priv);
> >>> +	if (IS_ERR(mgr))
> >>> +		return PTR_ERR(mgr);
> >>>  
> >>> -	return devm_fpga_mgr_register(dev, mgr);
> >>> +	return 0;
> >>>  }
> >>>  
> >>>  #ifdef CONFIG_OF
> >>> diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c
> >>> index a2cea500f7cc..9155e888a133 100644
> >>> --- a/drivers/fpga/stratix10-soc.c
> >>> +++ b/drivers/fpga/stratix10-soc.c
> >>> @@ -425,18 +425,11 @@ static int s10_probe(struct platform_device *pdev)
> >>>  
> >>>  	init_completion(&priv->status_return_completion);
> >>>  
> >>> -	mgr = fpga_mgr_create(dev, "Stratix10 SOC FPGA Manager",
> >>> -			      &s10_ops, priv);
> >>> -	if (!mgr) {
> >>> -		dev_err(dev, "unable to create FPGA manager\n");
> >>> -		ret = -ENOMEM;
> >>> -		goto probe_err;
> >>> -	}
> >>> -
> >>> -	ret = fpga_mgr_register(mgr);
> >>> -	if (ret) {
> >>> +	mgr = fpga_mgr_register(dev, "Stratix10 SOC FPGA Manager",
> >>> +				&s10_ops, priv);
> >>> +	if (IS_ERR(mgr)) {
> >>>  		dev_err(dev, "unable to register FPGA manager\n");
> >>> -		fpga_mgr_free(mgr);
> >>> +		ret = PTR_ERR(mgr);
> >>>  		goto probe_err;
> >>>  	}
> >>>  
> >>> @@ -454,7 +447,6 @@ static int s10_remove(struct platform_device *pdev)
> >>>  	struct s10_priv *priv = mgr->priv;
> >>>  
> >>>  	fpga_mgr_unregister(mgr);
> >>> -	fpga_mgr_free(mgr);
> >>>  	stratix10_svc_free_channel(priv->chan);
> >>>  
> >>>  	return 0;
> >>> diff --git a/drivers/fpga/ts73xx-fpga.c b/drivers/fpga/ts73xx-fpga.c
> >>> index 101f016c6ed8..5efbec2b9f9f 100644
> >>> --- a/drivers/fpga/ts73xx-fpga.c
> >>> +++ b/drivers/fpga/ts73xx-fpga.c
> >>> @@ -122,12 +122,12 @@ static int ts73xx_fpga_probe(struct platform_device *pdev)
> >>>  	if (IS_ERR(priv->io_base))
> >>>  		return PTR_ERR(priv->io_base);
> >>>  
> >>> -	mgr = devm_fpga_mgr_create(kdev, "TS-73xx FPGA Manager",
> >>> -				   &ts73xx_fpga_ops, priv);
> >>> -	if (!mgr)
> >>> -		return -ENOMEM;
> >>> +	mgr = devm_fpga_mgr_register(kdev, "TS-73xx FPGA Manager",
> >>> +				     &ts73xx_fpga_ops, priv);
> >>> +	if (IS_ERR(mgr))
> >>> +		return PTR_ERR(mgr);
> >>>  
> >>> -	return devm_fpga_mgr_register(kdev, mgr);
> >>> +	return 0;
> >>>  }
> >>>  
> >>>  static struct platform_driver ts73xx_fpga_driver = {
> >>> diff --git a/drivers/fpga/xilinx-spi.c b/drivers/fpga/xilinx-spi.c
> >>> index fee4d0abf6bf..efda475f7a98 100644
> >>> --- a/drivers/fpga/xilinx-spi.c
> >>> +++ b/drivers/fpga/xilinx-spi.c
> >>> @@ -247,13 +247,13 @@ static int xilinx_spi_probe(struct spi_device *spi)
> >>>  		return dev_err_probe(&spi->dev, PTR_ERR(conf->done),
> >>>  				     "Failed to get DONE gpio\n");
> >>>  
> >>> -	mgr = devm_fpga_mgr_create(&spi->dev,
> >>> -				   "Xilinx Slave Serial FPGA Manager",
> >>> -				   &xilinx_spi_ops, conf);
> >>> -	if (!mgr)
> >>> -		return -ENOMEM;
> >>> +	mgr = devm_fpga_mgr_register(&spi->dev,
> >>> +				     "Xilinx Slave Serial FPGA Manager",
> >>> +				     &xilinx_spi_ops, conf);
> >>> +	if (IS_ERR(mgr))
> >>> +		return PTR_ERR(mgr);
> >>>  
> >>> -	return devm_fpga_mgr_register(&spi->dev, mgr);
> >>> +	return 0;
> >>>  }
> >>>  
> >>>  static const struct of_device_id xlnx_spi_of_match[] = {
> >>> diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c
> >>> index 9b75bd4f93d8..426aa34c6a0d 100644
> >>> --- a/drivers/fpga/zynq-fpga.c
> >>> +++ b/drivers/fpga/zynq-fpga.c
> >>> @@ -609,20 +609,16 @@ static int zynq_fpga_probe(struct platform_device *pdev)
> >>>  
> >>>  	clk_disable(priv->clk);
> >>>  
> >>> -	mgr = devm_fpga_mgr_create(dev, "Xilinx Zynq FPGA Manager",
> >>> -				   &zynq_fpga_ops, priv);
> >>> -	if (!mgr)
> >>> -		return -ENOMEM;
> >>> -
> >>> -	platform_set_drvdata(pdev, mgr);
> >>> -
> >>> -	err = fpga_mgr_register(mgr);
> >>> -	if (err) {
> >>> +	mgr = fpga_mgr_register(dev, "Xilinx Zynq FPGA Manager",
> >>> +				&zynq_fpga_ops, priv);
> >>> +	if (IS_ERR(mgr)) {
> >>>  		dev_err(dev, "unable to register FPGA manager\n");
> >>>  		clk_unprepare(priv->clk);
> >>> -		return err;
> >>> +		return PTR_ERR(mgr);
> >>>  	}
> >>>  
> >>> +	platform_set_drvdata(pdev, mgr);
> >>> +
> >>>  	return 0;
> >>>  }
> >>>  
> >>> diff --git a/drivers/fpga/zynqmp-fpga.c b/drivers/fpga/zynqmp-fpga.c
> >>> index 125743c9797f..1a6d520985be 100644
> >>> --- a/drivers/fpga/zynqmp-fpga.c
> >>> +++ b/drivers/fpga/zynqmp-fpga.c
> >>> @@ -102,12 +102,12 @@ static int zynqmp_fpga_probe(struct platform_device *pdev)
> >>>  
> >>>  	priv->dev = dev;
> >>>  
> >>> -	mgr = devm_fpga_mgr_create(dev, "Xilinx ZynqMP FPGA Manager",
> >>> -				   &zynqmp_fpga_ops, priv);
> >>> -	if (!mgr)
> >>> -		return -ENOMEM;
> >>> +	mgr = devm_fpga_mgr_register(dev, "Xilinx ZynqMP FPGA Manager",
> >>> +				     &zynqmp_fpga_ops, priv);
> >>> +	if (IS_ERR(mgr))
> >>> +		return PTR_ERR(mgr);
> >>>  
> >>> -	return devm_fpga_mgr_register(dev, mgr);
> >>> +	return 0;
> >>>  }
> >>>  
> >>>  static const struct of_device_id zynqmp_fpga_of_match[] = {
> >>> diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
> >>> index 474c1f506307..d3c6f0153457 100644
> >>> --- a/include/linux/fpga/fpga-mgr.h
> >>> +++ b/include/linux/fpga/fpga-mgr.h
> >>> @@ -105,6 +105,17 @@ struct fpga_image_info {
> >>>  #endif
> >>>  };
> >>>  
> >>> +/**
> >>> + * struct fpga_compat_id - id for compatibility check
> >>> + *
> >>> + * @id_h: high 64bit of the compat_id
> >>> + * @id_l: low 64bit of the compat_id
> >>> + */
> >>> +struct fpga_compat_id {
> >>> +	u64 id_h;
> >>> +	u64 id_l;
> >>> +};
> >>> +
> >>>  /**
> >>>   * struct fpga_manager_ops - ops for low level fpga manager drivers
> >>>   * @initial_header_size: Maximum number of bytes that should be passed into write_init
> >>> @@ -116,6 +127,7 @@ struct fpga_image_info {
> >>>   * @write_complete: set FPGA to operating state after writing is done
> >>>   * @fpga_remove: optional: Set FPGA into a specific state during driver remove
> >>>   * @groups: optional attribute groups.
> >>> + * @compat_id: optional: FPGA manager id for compatibility check.
> >>>   *
> >>>   * fpga_manager_ops are the low level functions implemented by a specific
> >>>   * fpga manager driver.  The optional ones are tested for NULL before being
> >>> @@ -134,6 +146,7 @@ struct fpga_manager_ops {
> >>>  			      struct fpga_image_info *info);
> >>>  	void (*fpga_remove)(struct fpga_manager *mgr);
> >>>  	const struct attribute_group **groups;
> >>> +	struct fpga_compat_id *compat_id;
> >> We don't have to move compat_id here, keep it in struct fpga_mgr
> >>
> >> Thanks,
> >> Yilun
> >>
> >>>  };
> >>>  
> >>>  /* FPGA manager status: Partial/Full Reconfiguration errors */
> >>> @@ -143,24 +156,12 @@ struct fpga_manager_ops {
> >>>  #define FPGA_MGR_STATUS_IP_PROTOCOL_ERR		BIT(3)
> >>>  #define FPGA_MGR_STATUS_FIFO_OVERFLOW_ERR	BIT(4)
> >>>  
> >>> -/**
> >>> - * struct fpga_compat_id - id for compatibility check
> >>> - *
> >>> - * @id_h: high 64bit of the compat_id
> >>> - * @id_l: low 64bit of the compat_id
> >>> - */
> >>> -struct fpga_compat_id {
> >>> -	u64 id_h;
> >>> -	u64 id_l;
> >>> -};
> >>> -
> >>>  /**
> >>>   * struct fpga_manager - fpga manager structure
> >>>   * @name: name of low level fpga manager
> >>>   * @dev: fpga manager device
> >>>   * @ref_mutex: only allows one reference to fpga manager
> >>>   * @state: state of fpga manager
> >>> - * @compat_id: FPGA manager id for compatibility check.
> >>>   * @mops: pointer to struct of fpga manager ops
> >>>   * @priv: low level driver private date
> >>>   */
> >>> @@ -169,7 +170,6 @@ struct fpga_manager {
> >>>  	struct device dev;
> >>>  	struct mutex ref_mutex;
> >>>  	enum fpga_mgr_states state;
> >>> -	struct fpga_compat_id *compat_id;
> >>>  	const struct fpga_manager_ops *mops;
> >>>  	void *priv;
> >>>  };
> >>> @@ -191,17 +191,14 @@ struct fpga_manager *fpga_mgr_get(struct device *dev);
> >>>  
> >>>  void fpga_mgr_put(struct fpga_manager *mgr);
> >>>  
> >>> -struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name,
> >>> -				     const struct fpga_manager_ops *mops,
> >>> -				     void *priv);
> >>> -void fpga_mgr_free(struct fpga_manager *mgr);
> >>> -int fpga_mgr_register(struct fpga_manager *mgr);
> >>> +struct fpga_manager *fpga_mgr_register(struct device *dev, const char *name,
> >>> +				       const struct fpga_manager_ops *mops,
> >>> +				       void *priv);
> >>>  void fpga_mgr_unregister(struct fpga_manager *mgr);
> >>>  
> >>> -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr);
> >>> -
> >>> -struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name,
> >>> -					  const struct fpga_manager_ops *mops,
> >>> -					  void *priv);
> >>> +struct fpga_manager *
> >>> +devm_fpga_mgr_register(struct device *dev, const char *name,
> >>> +		       const struct fpga_manager_ops *mops,
> >>> +		       void *priv);
> >>>  
> >>>  #endif /*_LINUX_FPGA_MGR_H */
> >>> -- 
> >>> 2.25.1

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v5 2/3] fpga: bridge: Use standard dev_release for class driver
  2021-06-18 16:05     ` Russ Weight
@ 2021-06-21  7:38       ` Xu Yilun
  0 siblings, 0 replies; 19+ messages in thread
From: Xu Yilun @ 2021-06-21  7:38 UTC (permalink / raw)
  To: Russ Weight
  Cc: mdf, linux-fpga, trix, lgoncalv, hao.wu, matthew.gerlach, richard.gong

On Fri, Jun 18, 2021 at 09:05:01AM -0700, Russ Weight wrote:
> 
> 
> On 6/18/21 8:52 AM, Xu Yilun wrote:
> > On Wed, Jun 16, 2021 at 03:57:39PM -0700, Russ Weight wrote:
> >> The FPGA bridge class driver data structure is being treated as a
> >> managed resource instead of using standard dev_release call-back
> >> to release the class data structure. This change removes the
> >> managed resource code and combines the create() and register()
> >> functions into a single register() function.
> >>
> >> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
> >> ---
> >> v5:
> >>   - Rebased on top of recently accepted patches.
> >> v4:
> >>   - Restore the previous format for the Return value in the comment header
> >>     for fpga_bridge_register()
> >> v3:
> >>   - Cleaned up comment header for fpga_bridge_register()
> >>   - Fix error return values for fpga_bridge_register()
> >> v2:
> >>   - No changes
> >> ---
> >>  drivers/fpga/altera-fpga2sdram.c    |  12 +--
> >>  drivers/fpga/altera-freeze-bridge.c |  10 +--
> >>  drivers/fpga/altera-hps2fpga.c      |  12 +--
> >>  drivers/fpga/dfl-fme-br.c           |  10 +--
> >>  drivers/fpga/fpga-bridge.c          | 116 ++++++----------------------
> >>  drivers/fpga/xilinx-pr-decoupler.c  |  17 ++--
> >>  include/linux/fpga/fpga-bridge.h    |  12 +--
> >>  7 files changed, 49 insertions(+), 140 deletions(-)
> >>
> >> diff --git a/drivers/fpga/altera-fpga2sdram.c b/drivers/fpga/altera-fpga2sdram.c
> >> index a78e49c63c64..66063507116b 100644
> >> --- a/drivers/fpga/altera-fpga2sdram.c
> >> +++ b/drivers/fpga/altera-fpga2sdram.c
> >> @@ -121,17 +121,13 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev)
> >>  	/* Get f2s bridge configuration saved in handoff register */
> >>  	regmap_read(sysmgr, SYSMGR_ISWGRP_HANDOFF3, &priv->mask);
> >>  
> >> -	br = devm_fpga_bridge_create(dev, F2S_BRIDGE_NAME,
> >> -				     &altera_fpga2sdram_br_ops, priv);
> >> -	if (!br)
> >> -		return -ENOMEM;
> >> +	br = fpga_bridge_register(dev, F2S_BRIDGE_NAME,
> >> +				  &altera_fpga2sdram_br_ops, priv);
> >> +	if (IS_ERR(br))
> >> +		return PTR_ERR(mgr);
> >>  
> >>  	platform_set_drvdata(pdev, br);
> >>  
> >> -	ret = fpga_bridge_register(br);
> >> -	if (ret)
> >> -		return ret;
> >> -
> >>  	dev_info(dev, "driver initialized with handoff %08x\n", priv->mask);
> >>  
> >>  	if (!of_property_read_u32(dev->of_node, "bridge-enable", &enable)) {
> >> diff --git a/drivers/fpga/altera-freeze-bridge.c b/drivers/fpga/altera-freeze-bridge.c
> >> index dd58c4aea92e..bfbfa43cd05b 100644
> >> --- a/drivers/fpga/altera-freeze-bridge.c
> >> +++ b/drivers/fpga/altera-freeze-bridge.c
> >> @@ -244,14 +244,14 @@ static int altera_freeze_br_probe(struct platform_device *pdev)
> >>  
> >>  	priv->base_addr = base_addr;
> >>  
> >> -	br = devm_fpga_bridge_create(dev, FREEZE_BRIDGE_NAME,
> >> -				     &altera_freeze_br_br_ops, priv);
> >> -	if (!br)
> >> -		return -ENOMEM;
> >> +	br = fpga_bridge_register(dev, FREEZE_BRIDGE_NAME,
> >> +				  &altera_freeze_br_br_ops, priv);
> >> +	if (IS_ERR(br))
> >> +		return PTR_ERR(br);
> >>  
> >>  	platform_set_drvdata(pdev, br);
> >>  
> >> -	return fpga_bridge_register(br);
> >> +	return 0;
> >>  }
> >>  
> >>  static int altera_freeze_br_remove(struct platform_device *pdev)
> >> diff --git a/drivers/fpga/altera-hps2fpga.c b/drivers/fpga/altera-hps2fpga.c
> >> index 77b95f251821..aa758426c22b 100644
> >> --- a/drivers/fpga/altera-hps2fpga.c
> >> +++ b/drivers/fpga/altera-hps2fpga.c
> >> @@ -180,19 +180,15 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev)
> >>  		}
> >>  	}
> >>  
> >> -	br = devm_fpga_bridge_create(dev, priv->name,
> >> -				     &altera_hps2fpga_br_ops, priv);
> >> -	if (!br) {
> >> -		ret = -ENOMEM;
> >> +	br = fpga_bridge_register(dev, priv->name,
> >> +				  &altera_hps2fpga_br_ops, priv);
> >> +	if (IS_ERR(br)) {
> >> +		ret = PTR_ERR(br);
> >>  		goto err;
> >>  	}
> >>  
> >>  	platform_set_drvdata(pdev, br);
> >>  
> >> -	ret = fpga_bridge_register(br);
> >> -	if (ret)
> >> -		goto err;
> >> -
> >>  	return 0;
> >>  
> >>  err:
> >> diff --git a/drivers/fpga/dfl-fme-br.c b/drivers/fpga/dfl-fme-br.c
> >> index 3ff9f3a687ce..808d1f4d76df 100644
> >> --- a/drivers/fpga/dfl-fme-br.c
> >> +++ b/drivers/fpga/dfl-fme-br.c
> >> @@ -68,14 +68,14 @@ static int fme_br_probe(struct platform_device *pdev)
> >>  
> >>  	priv->pdata = dev_get_platdata(dev);
> >>  
> >> -	br = devm_fpga_bridge_create(dev, "DFL FPGA FME Bridge",
> >> -				     &fme_bridge_ops, priv);
> >> -	if (!br)
> >> -		return -ENOMEM;
> >> +	br = fpga_bridge_register(dev, "DFL FPGA FME Bridge",
> >> +				  &fme_bridge_ops, priv);
> >> +	if (IS_ERR(br))
> >> +		return PTR_ERR(br);
> >>  
> >>  	platform_set_drvdata(pdev, br);
> >>  
> >> -	return fpga_bridge_register(br);
> >> +	return 0;
> >>  }
> >>  
> >>  static int fme_br_remove(struct platform_device *pdev)
> >> diff --git a/drivers/fpga/fpga-bridge.c b/drivers/fpga/fpga-bridge.c
> >> index 798f55670646..15be378b64ab 100644
> >> --- a/drivers/fpga/fpga-bridge.c
> >> +++ b/drivers/fpga/fpga-bridge.c
> >> @@ -312,36 +312,35 @@ static struct attribute *fpga_bridge_attrs[] = {
> >>  ATTRIBUTE_GROUPS(fpga_bridge);
> >>  
> >>  /**
> >> - * fpga_bridge_create - create and initialize a struct fpga_bridge
> >> + * fpga_bridge_register - create and register a FPGA Bridge device
> >>   * @parent:	FPGA bridge device from pdev
> >>   * @name:	FPGA bridge name
> >>   * @br_ops:	pointer to structure of fpga bridge ops
> >>   * @priv:	FPGA bridge private data
> >>   *
> >> - * The caller of this function is responsible for freeing the bridge with
> >> - * fpga_bridge_free().  Using devm_fpga_bridge_create() instead is recommended.
> >> - *
> >> - * Return: struct fpga_bridge or NULL
> >> + * Return: struct fpga_bridge pointer or ERR_PTR()
> >>   */
> >> -struct fpga_bridge *fpga_bridge_create(struct device *parent, const char *name,
> >> -				       const struct fpga_bridge_ops *br_ops,
> >> -				       void *priv)
> >> +struct fpga_bridge *fpga_bridge_register(struct device *parent, const char *name,
> >> +					 const struct fpga_bridge_ops *br_ops,
> >> +					 void *priv)
> > Is it better we use the same style as fpga_mgr_register?
> 
> Can you explain? Do you mean the suggestions you are making for passing
> a new info structure?

Yes, we may discuss in Patch #1, bridge & region class are similar
cases.

> 
> >
> > Thanks,
> > Yilun
> >
> >>  {
> >>  	struct fpga_bridge *bridge;
> >>  	int id, ret;
> >>  
> >>  	if (!name || !strlen(name)) {
> >>  		dev_err(parent, "Attempt to register with no name!\n");
> >> -		return NULL;
> >> +		return ERR_PTR(-EINVAL);
> >>  	}
> >>  
> >>  	bridge = kzalloc(sizeof(*bridge), GFP_KERNEL);
> >>  	if (!bridge)
> >> -		return NULL;
> >> +		return ERR_PTR(-ENOMEM);
> >>  
> >>  	id = ida_simple_get(&fpga_bridge_ida, 0, 0, GFP_KERNEL);
> >> -	if (id < 0)
> >> +	if (id < 0) {
> >> +		ret = id;
> >>  		goto error_kfree;
> >> +	}
> >>  
> >>  	mutex_init(&bridge->mutex);
> >>  	INIT_LIST_HEAD(&bridge->node);
> >> @@ -350,17 +349,23 @@ struct fpga_bridge *fpga_bridge_create(struct device *parent, const char *name,
> >>  	bridge->br_ops = br_ops;
> >>  	bridge->priv = priv;
> >>  
> >> -	device_initialize(&bridge->dev);
> >>  	bridge->dev.groups = br_ops->groups;
> >>  	bridge->dev.class = fpga_bridge_class;
> >>  	bridge->dev.parent = parent;
> >>  	bridge->dev.of_node = parent->of_node;
> >>  	bridge->dev.id = id;
> >> +	of_platform_populate(bridge->dev.of_node, NULL, NULL, &bridge->dev);
> >>  
> >>  	ret = dev_set_name(&bridge->dev, "br%d", id);
> >>  	if (ret)
> >>  		goto error_device;
> >>  
> >> +	ret = device_register(&bridge->dev);
> >> +	if (ret) {
> >> +		put_device(&bridge->dev);
> >> +		return ERR_PTR(ret);
> >> +	}
> >> +
> >>  	return bridge;
> >>  
> >>  error_device:
> >> @@ -368,88 +373,7 @@ struct fpga_bridge *fpga_bridge_create(struct device *parent, const char *name,
> >>  error_kfree:
> >>  	kfree(bridge);
> >>  
> >> -	return NULL;
> >> -}
> >> -EXPORT_SYMBOL_GPL(fpga_bridge_create);
> >> -
> >> -/**
> >> - * fpga_bridge_free - free an fpga bridge created by fpga_bridge_create()
> >> - * @bridge:	FPGA bridge struct
> >> - */
> >> -void fpga_bridge_free(struct fpga_bridge *bridge)
> >> -{
> >> -	ida_simple_remove(&fpga_bridge_ida, bridge->dev.id);
> >> -	kfree(bridge);
> >> -}
> >> -EXPORT_SYMBOL_GPL(fpga_bridge_free);
> >> -
> >> -static void devm_fpga_bridge_release(struct device *dev, void *res)
> >> -{
> >> -	struct fpga_bridge *bridge = *(struct fpga_bridge **)res;
> >> -
> >> -	fpga_bridge_free(bridge);
> >> -}
> >> -
> >> -/**
> >> - * devm_fpga_bridge_create - create and init a managed struct fpga_bridge
> >> - * @parent:	FPGA bridge device from pdev
> >> - * @name:	FPGA bridge name
> >> - * @br_ops:	pointer to structure of fpga bridge ops
> >> - * @priv:	FPGA bridge private data
> >> - *
> >> - * This function is intended for use in an FPGA bridge driver's probe function.
> >> - * After the bridge driver creates the struct with devm_fpga_bridge_create(), it
> >> - * should register the bridge with fpga_bridge_register().  The bridge driver's
> >> - * remove function should call fpga_bridge_unregister().  The bridge 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_bridge_register(), the struct will still get cleaned up.
> >> - *
> >> - *  Return: struct fpga_bridge or NULL
> >> - */
> >> -struct fpga_bridge
> >> -*devm_fpga_bridge_create(struct device *parent, const char *name,
> >> -			 const struct fpga_bridge_ops *br_ops, void *priv)
> >> -{
> >> -	struct fpga_bridge **ptr, *bridge;
> >> -
> >> -	ptr = devres_alloc(devm_fpga_bridge_release, sizeof(*ptr), GFP_KERNEL);
> >> -	if (!ptr)
> >> -		return NULL;
> >> -
> >> -	bridge = fpga_bridge_create(parent, name, br_ops, priv);
> >> -	if (!bridge) {
> >> -		devres_free(ptr);
> >> -	} else {
> >> -		*ptr = bridge;
> >> -		devres_add(parent, ptr);
> >> -	}
> >> -
> >> -	return bridge;
> >> -}
> >> -EXPORT_SYMBOL_GPL(devm_fpga_bridge_create);
> >> -
> >> -/**
> >> - * fpga_bridge_register - register an FPGA bridge
> >> - *
> >> - * @bridge: FPGA bridge struct
> >> - *
> >> - * Return: 0 for success, error code otherwise.
> >> - */
> >> -int fpga_bridge_register(struct fpga_bridge *bridge)
> >> -{
> >> -	struct device *dev = &bridge->dev;
> >> -	int ret;
> >> -
> >> -	ret = device_add(dev);
> >> -	if (ret)
> >> -		return ret;
> >> -
> >> -	of_platform_populate(dev->of_node, NULL, NULL, dev);
> >> -
> >> -	dev_info(dev->parent, "fpga bridge [%s] registered\n", bridge->name);
> >> -
> >> -	return 0;
> >> +	return ERR_PTR(ret);
> >>  }
> >>  EXPORT_SYMBOL_GPL(fpga_bridge_register);
> >>  
> >> @@ -475,6 +399,10 @@ EXPORT_SYMBOL_GPL(fpga_bridge_unregister);
> >>  
> >>  static void fpga_bridge_dev_release(struct device *dev)
> >>  {
> >> +	struct fpga_bridge *bridge = to_fpga_bridge(dev);
> >> +
> >> +	ida_simple_remove(&fpga_bridge_ida, bridge->dev.id);
> >> +	kfree(bridge);
> >>  }
> >>  
> >>  static int __init fpga_bridge_dev_init(void)
> >> diff --git a/drivers/fpga/xilinx-pr-decoupler.c b/drivers/fpga/xilinx-pr-decoupler.c
> >> index ea2bde6e5bc4..c004e52b9464 100644
> >> --- a/drivers/fpga/xilinx-pr-decoupler.c
> >> +++ b/drivers/fpga/xilinx-pr-decoupler.c
> >> @@ -138,22 +138,17 @@ static int xlnx_pr_decoupler_probe(struct platform_device *pdev)
> >>  
> >>  	clk_disable(priv->clk);
> >>  
> >> -	br = devm_fpga_bridge_create(&pdev->dev, priv->ipconfig->name,
> >> -				     &xlnx_pr_decoupler_br_ops, priv);
> >> -	if (!br) {
> >> -		err = -ENOMEM;
> >> -		goto err_clk;
> >> -	}
> >> -
> >> -	platform_set_drvdata(pdev, br);
> >> -
> >> -	err = fpga_bridge_register(br);
> >> -	if (err) {
> >> +	br = fpga_bridge_register(&pdev->dev, priv->ipconfig->name,
> >> +				  &xlnx_pr_decoupler_br_ops, priv);
> >> +	if (IS_ERR(br)) {
> >> +		err = PTR_ERR(br);
> >>  		dev_err(&pdev->dev, "unable to register %s",
> >>  			priv->ipconfig->name);
> >>  		goto err_clk;
> >>  	}
> >>  
> >> +	platform_set_drvdata(pdev, br);
> >> +
> >>  	return 0;
> >>  
> >>  err_clk:
> >> diff --git a/include/linux/fpga/fpga-bridge.h b/include/linux/fpga/fpga-bridge.h
> >> index 6c3c28806ff1..2f0eb62c49f5 100644
> >> --- a/include/linux/fpga/fpga-bridge.h
> >> +++ b/include/linux/fpga/fpga-bridge.h
> >> @@ -62,15 +62,9 @@ int of_fpga_bridge_get_to_list(struct device_node *np,
> >>  			       struct fpga_image_info *info,
> >>  			       struct list_head *bridge_list);
> >>  
> >> -struct fpga_bridge *fpga_bridge_create(struct device *dev, const char *name,
> >> -				       const struct fpga_bridge_ops *br_ops,
> >> -				       void *priv);
> >> -void fpga_bridge_free(struct fpga_bridge *br);
> >> -int fpga_bridge_register(struct fpga_bridge *br);
> >> +struct fpga_bridge *fpga_bridge_register(struct device *dev, const char *name,
> >> +					 const struct fpga_bridge_ops *br_ops,
> >> +					 void *priv);
> >>  void fpga_bridge_unregister(struct fpga_bridge *br);
> >>  
> >> -struct fpga_bridge
> >> -*devm_fpga_bridge_create(struct device *dev, const char *name,
> >> -			 const struct fpga_bridge_ops *br_ops, void *priv);
> >> -
> >>  #endif /* _LINUX_FPGA_BRIDGE_H */
> >> -- 
> >> 2.25.1

^ permalink raw reply	[flat|nested] 19+ messages in thread

* RE: [PATCH v5 1/3] fpga: mgr: Use standard dev_release for class driver
  2021-06-18 22:01           ` Moritz Fischer
@ 2021-06-21  9:48             ` Wu, Hao
  0 siblings, 0 replies; 19+ messages in thread
From: Wu, Hao @ 2021-06-21  9:48 UTC (permalink / raw)
  To: Moritz Fischer, Tom Rix
  Cc: Weight, Russell H, Xu, Yilun, linux-fpga, lgoncalv, Gerlach,
	Matthew, Gong, Richard

> On Fri, Jun 18, 2021 at 01:39:42PM -0700, Tom Rix wrote:
> >
> > On 6/18/21 10:58 AM, Russ Weight wrote:
> > >
> > > On 6/18/21 9:03 AM, Russ Weight wrote:
> > > > On 6/18/21 8:45 AM, Xu Yilun wrote:
> > > > > On Wed, Jun 16, 2021 at 03:57:38PM -0700, Russ Weight wrote:
> > > > > > The FPGA manager class driver data structure is being treated as a
> > > > > > managed resource instead of using standard dev_release call-back
> > > > > > to release the class data structure. This change removes the
> > > > > > managed resource code for the freeing of the class data structure
> > > > > > and combines the create() and register() functions into a single
> > > > > > register() function.
> > > > > >
> > > > > > The devm_fpga_mgr_register() function is retained.
> > > > > >
> > > > > > Signed-off-by: Russ Weight <russell.h.weight@intel.com>
> > > > > > ---
> > > > > > v5:
> > > > > >    - Rebased on top of recently accepted patches.
> > > > > >    - Removed compat_id from the fpga_mgr_register() parameter list
> > > > > >      and added it to the fpga_manager_ops structure. This also required
> > > > > My reason for this change is, we could avoid further change of the
> > > > > fpga_mgr_register() API if we add new input parameters later on.
> > > > With this patchset, changes are only required for the callers
> > > > that use the new parameters.
> > > >
> > > > > >      dynamically allocating the dfl-fme-ops structure in order to add
> > > > > >      the appropriate compat_id.
> > > > > But enforcing the dynamical allocation of the parameters is not prefered
> > > > > to me. How about a dedicated structure that wraps all the needed
> > > > > parameters:
> > > > >
> > > > > struct fpga_mgr_info {
> > > > > 	const char *name;
> > > > > 	const struct fpga_manager_ops *mops;
> > > > > 	const struct fpga_compat_id *compat_id;
> > > > > 	const void *priv;
> > > > > };
> > > > >
> > > > > Then We can simply define a local variable of this struct for
> > > > > fpga_mgr_register().
> > > > >
> > > > > more details inline.
> > > > I agree the at the dynamic allocation is not preferred, but it is only
> > > > required if compat_id is used. In all other cases, the static structure
> > > > can continue to be used. In otherwords, only one caller is affected.
> > > > > > v4:
> > > > > >    - Added the compat_id parameter to fpga_mgr_register() and
> > > > > >      devm_fpga_mgr_register() to ensure that the compat_id is set
> before
> > > > > >      the device_register() call.
> > > > > > v3:
> > > > > >    - Cleaned up comment header for fpga_mgr_register()
> > > > > >    - Fix error return on ida_simple_get() failure
> > > > > > v2:
> > > > > >    - Restored devm_fpga_mgr_register() functionality, adapted for the
> combined
> > > > > >      create/register functionality.
> > > > > >    - All previous callers of devm_fpga_mgr_register() will continue to call
> > > > > >      devm_fpga_mgr_register().
> > > > > >    - replaced unnecessary ternary operators in return statements with
> standard
> > > > > >      if conditions.
> > > > > > ---
> > > > > >   drivers/fpga/altera-cvp.c        |  12 +--
> > > > > >   drivers/fpga/altera-pr-ip-core.c |   8 +-
> > > > > >   drivers/fpga/altera-ps-spi.c     |  10 +-
> > > > > >   drivers/fpga/dfl-fme-mgr.c       |  52 ++++++----
> > > > > >   drivers/fpga/dfl-fme-region.c    |   2 +-
> > > > > >   drivers/fpga/fpga-mgr.c          | 163 ++++++++-----------------------
> > > > > >   drivers/fpga/ice40-spi.c         |  10 +-
> > > > > >   drivers/fpga/machxo2-spi.c       |  10 +-
> > > > > >   drivers/fpga/socfpga-a10.c       |  16 ++-
> > > > > >   drivers/fpga/socfpga.c           |  10 +-
> > > > > >   drivers/fpga/stratix10-soc.c     |  16 +--
> > > > > >   drivers/fpga/ts73xx-fpga.c       |  10 +-
> > > > > >   drivers/fpga/xilinx-spi.c        |  12 +--
> > > > > >   drivers/fpga/zynq-fpga.c         |  16 ++-
> > > > > >   drivers/fpga/zynqmp-fpga.c       |  10 +-
> > > > > >   include/linux/fpga/fpga-mgr.h    |  43 ++++----
> > > > > >   16 files changed, 153 insertions(+), 247 deletions(-)
> > > > > >
> > > > > > diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c
> > > > > > index ccf4546eff29..4ffb9da537d8 100644
> > > > > > --- a/drivers/fpga/altera-cvp.c
> > > > > > +++ b/drivers/fpga/altera-cvp.c
> > > > > > @@ -652,19 +652,15 @@ static int altera_cvp_probe(struct pci_dev
> *pdev,
> > > > > >   	snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s @%s",
> > > > > >   		 ALTERA_CVP_MGR_NAME, pci_name(pdev));
> > > > > > -	mgr = devm_fpga_mgr_create(&pdev->dev, conf->mgr_name,
> > > > > > -				   &altera_cvp_ops, conf);
> > > > > > -	if (!mgr) {
> > > > > > -		ret = -ENOMEM;
> > > > > > +	mgr = fpga_mgr_register(&pdev->dev, conf->mgr_name,
> > > > > > +				&altera_cvp_ops, conf);
> > > > > > +	if (IS_ERR(mgr)) {
> > > > > > +		ret = PTR_ERR(mgr);
> > > > > >   		goto err_unmap;
> > > > > >   	}
> > > > > >   	pci_set_drvdata(pdev, mgr);
> > > > > > -	ret = fpga_mgr_register(mgr);
> > > > > > -	if (ret)
> > > > > > -		goto err_unmap;
> > > > > > -
> > > > > >   	return 0;
> > > > > >   err_unmap:
> > > > > > diff --git a/drivers/fpga/altera-pr-ip-core.c b/drivers/fpga/altera-pr-ip-
> core.c
> > > > > > index dfdf21ed34c4..17babf974852 100644
> > > > > > --- a/drivers/fpga/altera-pr-ip-core.c
> > > > > > +++ b/drivers/fpga/altera-pr-ip-core.c
> > > > > > @@ -191,11 +191,11 @@ int alt_pr_register(struct device *dev, void
> __iomem *reg_base)
> > > > > >   		(val & ALT_PR_CSR_STATUS_MSK) >>
> ALT_PR_CSR_STATUS_SFT,
> > > > > >   		(int)(val & ALT_PR_CSR_PR_START));
> > > > > > -	mgr = devm_fpga_mgr_create(dev, dev_name(dev),
> &alt_pr_ops, priv);
> > > > > > -	if (!mgr)
> > > > > > -		return -ENOMEM;
> > > > > > +	mgr = devm_fpga_mgr_register(dev, dev_name(dev),
> &alt_pr_ops, priv);
> > > > > > +	if (IS_ERR(mgr))
> > > > > > +		return PTR_ERR(mgr);
> > > > > > -	return devm_fpga_mgr_register(dev, mgr);
> > > > > > +	return 0;
> > > > > >   }
> > > > > >   EXPORT_SYMBOL_GPL(alt_pr_register);
> > > > > > diff --git a/drivers/fpga/altera-ps-spi.c b/drivers/fpga/altera-ps-spi.c
> > > > > > index 23bfd4d1ad0f..d3f77b0312b2 100644
> > > > > > --- a/drivers/fpga/altera-ps-spi.c
> > > > > > +++ b/drivers/fpga/altera-ps-spi.c
> > > > > > @@ -302,12 +302,12 @@ static int altera_ps_probe(struct spi_device
> *spi)
> > > > > >   	snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s %s",
> > > > > >   		 dev_driver_string(&spi->dev), dev_name(&spi->dev));
> > > > > > -	mgr = devm_fpga_mgr_create(&spi->dev, conf->mgr_name,
> > > > > > -				   &altera_ps_ops, conf);
> > > > > > -	if (!mgr)
> > > > > > -		return -ENOMEM;
> > > > > > +	mgr = devm_fpga_mgr_register(&spi->dev, conf->mgr_name,
> > > > > > +				     &altera_ps_ops, conf);
> > > > > > +	if (IS_ERR(mgr))
> > > > > > +		return PTR_ERR(mgr);
> > > > > > -	return devm_fpga_mgr_register(&spi->dev, mgr);
> > > > > > +	return 0;
> > > > > >   }
> > > > > >   static const struct spi_device_id altera_ps_spi_ids[] = {
> > > > > > diff --git a/drivers/fpga/dfl-fme-mgr.c b/drivers/fpga/dfl-fme-mgr.c
> > > > > > index d5861d13b306..1b93bc292dbe 100644
> > > > > > --- a/drivers/fpga/dfl-fme-mgr.c
> > > > > > +++ b/drivers/fpga/dfl-fme-mgr.c
> > > > > > @@ -264,14 +264,6 @@ static u64 fme_mgr_status(struct
> fpga_manager *mgr)
> > > > > >   	return pr_error_to_mgr_status(priv->pr_error);
> > > > > >   }
> > > > > > -static const struct fpga_manager_ops fme_mgr_ops = {
> > > > > > -	.write_init = fme_mgr_write_init,
> > > > > > -	.write = fme_mgr_write,
> > > > > > -	.write_complete = fme_mgr_write_complete,
> > > > > > -	.state = fme_mgr_state,
> > > > > > -	.status = fme_mgr_status,
> > > > > > -};
> > > > > > -
> > > > > >   static void fme_mgr_get_compat_id(void __iomem *fme_pr,
> > > > > >   				  struct fpga_compat_id *id)
> > > > > >   {
> > > > > > @@ -279,10 +271,34 @@ static void fme_mgr_get_compat_id(void
> __iomem *fme_pr,
> > > > > >   	id->id_h = readq(fme_pr + FME_PR_INTFC_ID_H);
> > > > > >   }
> > > > > > +static struct fpga_manager_ops *fme_mgr_get_ops(struct device
> *dev,
> > > > > > +						struct fme_mgr_priv
> *priv)
> > > > > > +{
> > > > > > +	struct fpga_manager_ops *ops;
> > > > > > +
> > > > > > +	ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL);
> > > > > > +	if (!ops)
> > > > > > +		return NULL;
> > > > > > +
> > > > > > +	ops->compat_id = devm_kzalloc(dev, sizeof(struct
> fpga_compat_id),
> > > > > > +				      GFP_KERNEL);
> > > > > > +	if (!ops->compat_id)
> > > > > > +		return NULL;
> > > > > > +
> > > > > > +	fme_mgr_get_compat_id(priv->ioaddr, ops->compat_id);
> > > > > > +	ops->write_init = fme_mgr_write_init;
> > > > > > +	ops->write = fme_mgr_write;
> > > > > > +	ops->write_complete = fme_mgr_write_complete;
> > > > > > +	ops->state = fme_mgr_state;
> > > > > > +	ops->status = fme_mgr_status;
> > > > > > +
> > > > > > +	return ops;
> > > > > > +}
> > > What do other's think? Is it better to dynamically allocate the ops structure
> > > for users of compat_id (just one user at this time)? Or better to create an
> > > info structure on the stack for all callers? See above for an example of a
> > > dynamically allocated the ops structure.
> > >
> > > To me, using the ops structure seems more standard, and the dynamic
> allocation,
> > > while not optimal, does not require much more space or complexity than the
> static
> > > allocation. At this time it only affects one caller.
> > >
> > > Adding the info structure as a parameter to the register() functions adds a
> > > little more complexity to all callers, whether or not they use the dynamic
> > > elements of the structure.
> >
> > Looks like dfl is the only user of compat_id.
> >
> > A board specific value does not belong in a common structure, it belongs in
> > a board structure
> >
> > Move compat_id out of fpga-mgr.h and into dfl.h
> >
> > In dfl- you can do whatever you want.
> 
> Agreed. You'll have to deal with DFL specific region, that directly
> accesses mgr->compat_id.
> 
> But yes, this should move to DFL, nobody else is using it.

Actually compat_id is used for hardware compatible checking, we consider this
as a common need previously. E.g. PR application can get this compat_id from
a common sysfs interface and compare it with headers of bitstream image before
start PR. We know some hardware doesn't need this kind of checking as it can be
recovered by from bad condition easily but some hardware can't. This is why
compatible checking is important for them. In early submission of DFL, this
interface was private, but moved out later as a common one per suggestion
from community.

Currently compat_id is defined as per-region item, and in DFL, all regions under
the same fpga-mgr shares the same compat_id of the fpga-mgr. If we really want
to remove compat_id from fpga-region and fpga-mgr data structure, then we
may need to add new ops to fpga-mgr / region to get compat_id (dfl provides
those callbacks, and move compact_id into private data). But I am not sure if
we really have to do this, if anything just let me know.

Thanks
Hao

> 
> - Moritz

^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2021-06-21  9:48 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-06-16 22:57 [PATCH v5 0/3] fpga: Use standard class dev_release function Russ Weight
2021-06-16 22:57 ` [PATCH v5 1/3] fpga: mgr: Use standard dev_release for class driver Russ Weight
2021-06-18 15:45   ` Xu Yilun
2021-06-18 16:03     ` Russ Weight
2021-06-18 17:58       ` Russ Weight
2021-06-18 20:39         ` Tom Rix
2021-06-18 22:01           ` Moritz Fischer
2021-06-21  9:48             ` Wu, Hao
2021-06-18 22:31           ` Russ Weight
2021-06-21  6:39             ` Xu Yilun
2021-06-21  6:47         ` Wu, Hao
2021-06-21  7:36         ` Xu Yilun
2021-06-16 22:57 ` [PATCH v5 2/3] fpga: bridge: " Russ Weight
2021-06-18 15:52   ` Xu Yilun
2021-06-18 16:05     ` Russ Weight
2021-06-21  7:38       ` Xu Yilun
2021-06-16 22:57 ` [PATCH v5 3/3] fpga: region: " Russ Weight
2021-06-18 15:54   ` Xu Yilun
2021-06-18 16:06     ` Russ Weight

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).