diff options
-rw-r--r-- | drivers/usb/host/ehci-generic.c | 118 |
1 files changed, 95 insertions, 23 deletions
diff --git a/drivers/usb/host/ehci-generic.c b/drivers/usb/host/ehci-generic.c index 2116ae1f11..e058445dca 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, |