usb: host: ehci-generic: add error path and .remove callback

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@st.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
Patrice Chotard 2017-07-18 11:57:10 +02:00 committed by Marek Vasut
parent 10bb775e92
commit a1cee8e808

View File

@ -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;
int i, err, ret, clock_nb, reset_nb;
for (i = 0; ; i++) {
struct clk clk;
int ret;
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;
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);
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;
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);
for (i = 0; i < reset_nb; i++) {
err = reset_get_by_index(dev, i, &priv->resets[i]);
if (err < 0)
break;
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,