
From: Patrice Chotard patrice.chotard@st.com
use list to save reference to deasserted resets in order to assert them in case of error during probe() or during driver removal.
Signed-off-by: Patrice Chotard patrice.chotard@st.com ---
v3: _ extract in this patch the RESET support add-on from previous patch 5 _ keep deassrted resets reference in list in order to assert resets in error path or in .remove callback
v2: _ add error path management _ add .remove callback
drivers/usb/host/ohci-generic.c | 78 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+)
diff --git a/drivers/usb/host/ohci-generic.c b/drivers/usb/host/ohci-generic.c index a6d89a8..bf14ab7 100644 --- a/drivers/usb/host/ohci-generic.c +++ b/drivers/usb/host/ohci-generic.c @@ -7,6 +7,7 @@ #include <common.h> #include <clk.h> #include <dm.h> +#include <reset.h> #include "ohci.h"
#if !defined(CONFIG_USB_OHCI_NEW) @@ -18,11 +19,43 @@ struct ohci_clock { struct list_head list; };
+struct ohci_reset { + struct reset_ctl *reset; + struct list_head list; +}; + struct generic_ohci { ohci_t ohci; struct list_head clks; + struct list_head resets; };
+static int ohci_release_resets(struct generic_ohci *priv) +{ + struct ohci_reset *ohci_reset, *tmp; + struct reset_ctl *reset; + int ret; + + list_for_each_entry_safe(ohci_reset, tmp, &priv->resets, list) { + reset = ohci_reset->reset; + + ret = reset_request(reset); + if (ret) + return ret; + + ret = reset_assert(reset); + if (ret) + return ret; + + ret = reset_free(reset); + if (ret) + return ret; + + list_del(&(ohci_reset->list)); + } + return 0; +} + static int ohci_release_clocks(struct generic_ohci *priv) { struct ohci_clock *ohci_clock, *tmp; @@ -54,6 +87,7 @@ static int ohci_usb_probe(struct udevice *dev) int i, ret;
INIT_LIST_HEAD(&priv->clks); + INIT_LIST_HEAD(&priv->resets);
for (i = 0; ; i++) { struct ohci_clock *ohci_clock; @@ -89,8 +123,48 @@ static int ohci_usb_probe(struct udevice *dev) list_add(&ohci_clock->list, &priv->clks); }
+ for (i = 0; ; i++) { + struct ohci_reset *ohci_reset; + struct reset_ctl *reset; + + reset = devm_kmalloc(dev, sizeof(*reset), GFP_KERNEL); + if (!reset) { + error("Can't allocate resource\n"); + goto clk_err; + } + + 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); + goto reset_err; + } + reset_free(reset); + + /* + * add deasserted resets into resets list in order to be + * asserted later on ohci_usb_remove() call or in error + * path if needed + */ + ohci_reset = devm_kmalloc(dev, sizeof(*ohci_reset), GFP_KERNEL); + if (!ohci_reset) { + error("Can't allocate resource\n"); + goto reset_err; + } + ohci_reset->reset = reset; + list_add(&ohci_reset->list, &priv->resets); + } + + return ohci_register(dev, regs);
+reset_err: + ret = ohci_release_resets(priv); + if (ret) + return ret; clk_err: return ohci_release_clocks(priv); } @@ -104,6 +178,10 @@ static int ohci_usb_remove(struct udevice *dev) if (ret) return ret;
+ ret = ohci_release_resets(priv); + if (ret) + return ret; + return ohci_release_clocks(priv); }