
From: Patrice Chotard patrice.chotard@st.com
Add CLOCK, RESET and generic PHY frameworks support
Signed-off-by: Patrice Chotard patrice.chotard@st.com ---
v2: _ add error path management _ add .remove callback
drivers/usb/host/ohci-generic.c | 99 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 96 insertions(+), 3 deletions(-)
diff --git a/drivers/usb/host/ohci-generic.c b/drivers/usb/host/ohci-generic.c index f3307f4..f86e223 100644 --- a/drivers/usb/host/ohci-generic.c +++ b/drivers/usb/host/ohci-generic.c @@ -5,26 +5,119 @@ */
#include <common.h> +#include <clk.h> #include <dm.h> +#include <fdtdec.h> +#include <generic-phy.h> +#include <reset.h> + #include "ohci.h"
#if !defined(CONFIG_USB_OHCI_NEW) # error "Generic OHCI driver requires CONFIG_USB_OHCI_NEW" #endif
+#define OHCI_MAX_CLOCKS 3 +#define OHCI_MAX_RESETS 3 + struct generic_ohci { ohci_t ohci; + struct clk clks[OHCI_MAX_CLOCKS]; + struct reset_ctl resets[OHCI_MAX_RESETS]; + struct phy phy; };
+static void ohci_assert_resets(struct udevice *dev) { + struct generic_ohci *priv = dev_get_priv(dev); + struct reset_ctl reset; + int i; + + for (i = OHCI_MAX_RESETS; i >= 0; --i) { + reset = priv->resets[i]; + + if (reset.dev) { + reset_request(&reset); + reset_assert(&reset); + reset_free(&reset); + } + } +} + +static void ohci_disable_clocks(struct udevice *dev) { + struct generic_ohci *priv = dev_get_priv(dev); + struct clk clk; + int i; + + for (i = OHCI_MAX_CLOCKS; i >= 0; --i) { + clk = priv->clks[i]; + + if (clk.dev) { + clk_request(clk.dev, &clk); + clk_disable(&clk); + clk_free(&clk); + } + } +} + static int ohci_usb_probe(struct udevice *dev) { struct ohci_regs *regs = (struct ohci_regs *)dev_get_addr(dev); + struct generic_ohci *priv = dev_get_priv(dev); + int i, ret; + + for (i = 0; i < OHCI_MAX_CLOCKS; i++) { + struct clk clk = priv->clks[i];
- return ohci_register(dev, regs); + 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); + goto clk_err; + } + clk_free(&clk); + } + + for (i = 0; i < OHCI_MAX_RESETS ; i++) { + struct reset_ctl reset = priv->resets[i]; + + 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); + } + + if (!generic_phy_get_by_index(dev, 0, &priv->phy)) + if (generic_phy_init(&priv->phy)) + error("failed to init usb phy %d\n", i); + + ret = ohci_register(dev, regs); + if (!ret) + return ret; + + generic_phy_exit(&priv->phy); + +reset_err: + ohci_assert_resets(dev); +clk_err: + ohci_disable_clocks(dev); + + return ret; }
-static int ohci_usb_remove(struct udevice *dev) -{ +static int ohci_usb_remove(struct udevice *dev) { + struct generic_ohci *priv = dev_get_priv(dev); + + generic_phy_exit(&priv->phy); + ohci_assert_resets(dev); + ohci_disable_clocks(dev); + return ohci_deregister(dev); }