[U-Boot] [PATCH v10 06/10] usb: host: ehci-generic: add error path and .remove callback

patrice.chotard at st.com patrice.chotard at st.com
Thu Jul 6 07:42:27 UTC 2017


From: Patrice Chotard <patrice.chotard at st.com>

Use an array to save enabled clocks reference and deasserted resets
in order to respectively disabled and asserted them in case of error
during probe() or during driver removal.

Signed-off-by: Patrice Chotard <patrice.chotard at st.com>
---

v10:	_ none
v9:	_ remove useless reset_free() and clk_free() in case of reset and clock has been 
	  correctly get and deasserted/enabled

v8:	_ replace devm_kmalloc() by devm_kcalloc()
	_ fix error path by propagating initial error code until the end of probe()

v7:	_ replace clk_count() and reset_count() by ofnode_count_phandle_with_args()

v6:	_ none

v5: 	_ none

v4:	_ update the memory allocation for deasserted resets and enabled
	  clocks reference list. Replace lists by arrays.
	_ usage of new RESET and CLOCK methods clk_count(), reset_count(),
	  reset_assert_all() and clk_disable_all().

v3:	_ keep enabled clocks and deasserted resets reference in list in order to
	  disable clock or assert resets in error path or in .remove callback
	_ use struct generic_ehci * instead of struct udevice * as parameter for
	  ehci_release_resets() and ehci_release_clocks()
 drivers/usb/host/ehci-generic.c | 118 ++++++++++++++++++++++++++++++++--------
 1 file changed, 95 insertions(+), 23 deletions(-)

diff --git a/drivers/usb/host/ehci-generic.c b/drivers/usb/host/ehci-generic.c
index 2116ae1..e058445 100644
--- a/drivers/usb/host/ehci-generic.c
+++ b/drivers/usb/host/ehci-generic.c
@@ -6,6 +6,7 @@
 
 #include <common.h>
 #include <clk.h>
+#include <dm/ofnode.h>
 #include <reset.h>
 #include <asm/io.h>
 #include <dm.h>
@@ -18,43 +19,114 @@
  */
 struct generic_ehci {
 	struct ehci_ctrl ctrl;
+	struct clk *clocks;
+	struct reset_ctl *resets;
+	int clock_count;
+	int reset_count;
 };
 
 static int ehci_usb_probe(struct udevice *dev)
 {
+	struct generic_ehci *priv = dev_get_priv(dev);
 	struct ehci_hccr *hccr;
 	struct ehci_hcor *hcor;
-	int i;
-
-	for (i = 0; ; i++) {
-		struct clk clk;
-		int ret;
-
-		ret = clk_get_by_index(dev, i, &clk);
-		if (ret < 0)
-			break;
-		if (clk_enable(&clk))
-			error("failed to enable clock %d\n", i);
-		clk_free(&clk);
+	int i, err, ret, clock_nb, reset_nb;
+
+	err = 0;
+	priv->clock_count = 0;
+	clock_nb = ofnode_count_phandle_with_args(dev_ofnode(dev), "clocks",
+						  "#clock-cells");
+	if (clock_nb > 0) {
+		priv->clocks = devm_kcalloc(dev, clock_nb, sizeof(struct clk),
+					    GFP_KERNEL);
+		if (!priv->clocks)
+			return -ENOMEM;
+
+		for (i = 0; i < clock_nb; i++) {
+			err = clk_get_by_index(dev, i, &priv->clocks[i]);
+
+			if (err < 0)
+				break;
+			err = clk_enable(&priv->clocks[i]);
+			if (err) {
+				error("failed to enable clock %d\n", i);
+				clk_free(&priv->clocks[i]);
+				goto clk_err;
+			}
+			priv->clock_count++;
+		}
+	} else {
+		if (clock_nb != -ENOENT) {
+			error("failed to get clock phandle(%d)\n", clock_nb);
+			return clock_nb;
+		}
 	}
 
-	for (i = 0; ; i++) {
-		struct reset_ctl reset;
-		int ret;
+	priv->reset_count = 0;
+	reset_nb = ofnode_count_phandle_with_args(dev_ofnode(dev), "resets",
+						  "#reset-cells");
+	if (reset_nb > 0) {
+		priv->resets = devm_kcalloc(dev, reset_nb,
+					    sizeof(struct reset_ctl),
+					    GFP_KERNEL);
+		if (!priv->resets)
+			return -ENOMEM;
+
+		for (i = 0; i < reset_nb; i++) {
+			err = reset_get_by_index(dev, i, &priv->resets[i]);
+			if (err < 0)
+				break;
 
-		ret = reset_get_by_index(dev, i, &reset);
-		if (ret < 0)
-			break;
-		if (reset_deassert(&reset))
-			error("failed to deassert reset %d\n", i);
-		reset_free(&reset);
+			if (reset_deassert(&priv->resets[i])) {
+				error("failed to deassert reset %d\n", i);
+				reset_free(&priv->resets[i]);
+				goto reset_err;
+			}
+			priv->reset_count++;
+		}
+	} else {
+		if (reset_nb != -ENOENT) {
+			error("failed to get reset phandle(%d)\n", reset_nb);
+			goto clk_err;
+		}
 	}
 
 	hccr = map_physmem(devfdt_get_addr(dev), 0x100, MAP_NOCACHE);
 	hcor = (struct ehci_hcor *)((uintptr_t)hccr +
 				    HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
 
-	return ehci_register(dev, hccr, hcor, NULL, 0, USB_INIT_HOST);
+	err = ehci_register(dev, hccr, hcor, NULL, 0, USB_INIT_HOST);
+	if (err)
+		goto reset_err;
+
+	return 0;
+
+reset_err:
+	ret = reset_release_all(priv->resets, priv->reset_count);
+	if (ret)
+		error("failed to assert all resets\n");
+clk_err:
+	ret = clk_release_all(priv->clocks, priv->clock_count);
+	if (ret)
+		error("failed to disable all clocks\n");
+
+	return err;
+}
+
+static int ehci_usb_remove(struct udevice *dev)
+{
+	struct generic_ehci *priv = dev_get_priv(dev);
+	int ret;
+
+	ret = ehci_deregister(dev);
+	if (ret)
+		return ret;
+
+	ret =  reset_release_all(priv->resets, priv->reset_count);
+	if (ret)
+		return ret;
+
+	return clk_release_all(priv->clocks, priv->clock_count);
 }
 
 static const struct udevice_id ehci_usb_ids[] = {
@@ -67,7 +139,7 @@ U_BOOT_DRIVER(ehci_generic) = {
 	.id	= UCLASS_USB,
 	.of_match = ehci_usb_ids,
 	.probe = ehci_usb_probe,
-	.remove = ehci_deregister,
+	.remove = ehci_usb_remove,
 	.ops	= &ehci_usb_ops,
 	.priv_auto_alloc_size = sizeof(struct generic_ehci),
 	.flags	= DM_FLAG_ALLOC_PRIV_DMA,
-- 
1.9.1



More information about the U-Boot mailing list