[U-Boot] [PATCH v4 00/11] usb: Extend ehci and ohci generic drivers

From: Patrice Chotard patrice.chotard@st.com
This series improves generic ehci and ohci drivers by addition of : _ error path during probe (clocks, resets and phy release) _ .remove callback _ add generic PHY framework for both generic ehci and ohci drivers _ add RESET and CLOCK framework for generic ohci driver
To implement these features, some new methods are needed in RESET and CLOCKS frameworks : _ add reset_request(), reset_count() and reset_assert_all() methods in RESET framework _ add clk_count() and add clk_disable_all() methods in CLOCK framework
v4: _ add clk_disable_all() and reset_assert_all() methods into CLOCK and RESET framework as suggested by Simon Glass and Marek Vasut _ add reset_count() and clk_count() methods which returns respectively the number of resets and clocks declared into "resets" and "clocks" DT properties. This allows to allocate the right amount of memory to keep resets and clocks reference _ update the memory allocation for deasserted resets and enabled clocks reference list. Replace lists by arrays. 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 _ add missing commit message _ use struct generic_ehci * instead of struct udevice * as parameter for ehci_release_resets() and ehci_release_clocks() _ test return value on generic_phy_get_by_index() and generic_phy_init() _ split previous patch 5 in 3 independant patch for CLOCK, RESET and PHY support
v2: _ add needed reset_request() in RESET framework _ add error path in ehci/ohci-generic to disable clocks and to assert resets _ add .remove callback with clocks, resets and phy release _ split the replacement of printf() by error() in an independant patch
Patrice Chotard (11): reset: add reset_request() reset: add reset_count() reset: add reset_assert_all() clk: add clk_count() clk: add clk_disable_all() usb: host: ehci-generic: replace printf() by error() usb: host: ehci-generic: add error path and .remove callback usb: host: ehci-generic: add generic PHY support usb: host: ohci-generic: add CLOCK support usb: host: ohci-generic: add RESET support usb: host: ohci-generic: add generic PHY support
drivers/clk/clk-uclass.c | 34 ++++++++++ drivers/reset/reset-uclass.c | 45 +++++++++++++ drivers/usb/host/ehci-generic.c | 142 +++++++++++++++++++++++++++++++++------- drivers/usb/host/ohci-generic.c | 121 +++++++++++++++++++++++++++++++++- include/clk.h | 22 +++++++ include/reset.h | 37 +++++++++++ 6 files changed, 376 insertions(+), 25 deletions(-)

From: Patrice Chotard patrice.chotard@st.com
This is needed in error path to assert previously deasserted reset by using a saved reset_ctl reference.
Signed-off-by: Patrice Chotard patrice.chotard@st.com Reviewed-by: Simon Glass sjg@chromium.org ---
v4: _ none v3: _ none v2: _ none
drivers/reset/reset-uclass.c | 9 +++++++++ include/reset.h | 9 +++++++++ 2 files changed, 18 insertions(+)
diff --git a/drivers/reset/reset-uclass.c b/drivers/reset/reset-uclass.c index e92b24f..916f210 100644 --- a/drivers/reset/reset-uclass.c +++ b/drivers/reset/reset-uclass.c @@ -98,6 +98,15 @@ int reset_get_by_name(struct udevice *dev, const char *name, return reset_get_by_index(dev, index, reset_ctl); }
+int reset_request(struct reset_ctl *reset_ctl) +{ + struct reset_ops *ops = reset_dev_ops(reset_ctl->dev); + + debug("%s(reset_ctl=%p)\n", __func__, reset_ctl); + + return ops->request(reset_ctl); +} + int reset_free(struct reset_ctl *reset_ctl) { struct reset_ops *ops = reset_dev_ops(reset_ctl->dev); diff --git a/include/reset.h b/include/reset.h index f45fcf8..4f2e35f 100644 --- a/include/reset.h +++ b/include/reset.h @@ -100,6 +100,15 @@ int reset_get_by_name(struct udevice *dev, const char *name, struct reset_ctl *reset_ctl);
/** + * reset_request - Request a reset signal. + * + * @reset_ctl: A reset control struct. + * + * @return 0 if OK, or a negative error code. + */ +int reset_request(struct reset_ctl *reset_ctl); + +/** * reset_free - Free a previously requested reset signal. * * @reset_ctl: A reset control struct that was previously successfully

From: Patrice Chotard patrice.chotard@st.com
Add reset_count() method to be able to get the number of resets contained into a resets property. This will allow to allocate the right amount of memory in order to keep resets reference. These resets reference can be used later on error path or in .remove callback to release these resets.
Signed-off-by: Patrice Chotard patrice.chotard@st.com ---
v4: _ add reset_count() method
drivers/reset/reset-uclass.c | 14 ++++++++++++++ include/reset.h | 11 +++++++++++ 2 files changed, 25 insertions(+)
diff --git a/drivers/reset/reset-uclass.c b/drivers/reset/reset-uclass.c index 916f210..ebdeee5 100644 --- a/drivers/reset/reset-uclass.c +++ b/drivers/reset/reset-uclass.c @@ -98,6 +98,20 @@ int reset_get_by_name(struct udevice *dev, const char *name, return reset_get_by_index(dev, index, reset_ctl); }
+int reset_count(struct udevice *dev) +{ + int count; + struct fdtdec_phandle_args args; + + for (count = 0; ; count++) { + if (fdtdec_parse_phandle_with_args(gd->fdt_blob, + dev_of_offset(dev), + "resets", "#reset-cells", 0, + count, &args)) + return count; + } +} + int reset_request(struct reset_ctl *reset_ctl) { struct reset_ops *ops = reset_dev_ops(reset_ctl->dev); diff --git a/include/reset.h b/include/reset.h index 4f2e35f..e8e68b6 100644 --- a/include/reset.h +++ b/include/reset.h @@ -100,6 +100,17 @@ int reset_get_by_name(struct udevice *dev, const char *name, struct reset_ctl *reset_ctl);
/** + * reset_count - Get reset count contained in the "resets" property. + * + * This returns the count of reset found into the "resets" property. This + * allows to allocate the right amount of memory to keep reset reference. + * + * @dev: The client device. + * @return number of resets found. + */ +int reset_count(struct udevice *dev); + +/** * reset_request - Request a reset signal. * * @reset_ctl: A reset control struct.

From: Patrice Chotard patrice.chotard@st.com
Add reset_assert_all() method which Request/Assert/Free an array of resets signal that has been previously successfully requested by reset_get_by_*()
Signed-off-by: Patrice Chotard patrice.chotard@st.com ---
v4: _ add reset_assert_all() method as suggested by Marek Vasut and Simon Glass
drivers/reset/reset-uclass.c | 22 ++++++++++++++++++++++ include/reset.h | 17 +++++++++++++++++ 2 files changed, 39 insertions(+)
diff --git a/drivers/reset/reset-uclass.c b/drivers/reset/reset-uclass.c index ebdeee5..f1ab899 100644 --- a/drivers/reset/reset-uclass.c +++ b/drivers/reset/reset-uclass.c @@ -148,6 +148,28 @@ int reset_deassert(struct reset_ctl *reset_ctl) return ops->rst_deassert(reset_ctl); }
+int reset_assert_all(struct reset_ctl *reset_ctl, int count) +{ + int i, ret; + + for (i = 0; i < count; i++) { + debug("%s(reset_ctl[%d]=%p)\n", __func__, i, &reset_ctl[i]); + + ret = reset_request(&reset_ctl[i]); + if (ret) + return ret; + + ret = reset_assert(&reset_ctl[i]); + if (ret) + return ret; + + ret = reset_free(&reset_ctl[i]); + if (ret) + return ret; + } + return 0; +} + UCLASS_DRIVER(reset) = { .id = UCLASS_RESET, .name = "reset", diff --git a/include/reset.h b/include/reset.h index e8e68b6..fa3b4a4 100644 --- a/include/reset.h +++ b/include/reset.h @@ -155,6 +155,17 @@ int reset_assert(struct reset_ctl *reset_ctl); */ int reset_deassert(struct reset_ctl *reset_ctl);
+/** + * reset_assert_all - Request/Assert/Free resets. + * + * This function will request, assert and free array of clocks, + * + * @reset_ctl: A reset struct array that was previously successfully + * requested by reset_get_by_*(). + * @count Number of reset contained in the array + * @return 0 if OK, or a negative error code. + */ +int reset_assert_all(struct reset_ctl *reset_ctl, int count); #else static inline int reset_get_by_index(struct udevice *dev, int index, struct reset_ctl *reset_ctl) @@ -182,6 +193,12 @@ static inline int reset_deassert(struct reset_ctl *reset_ctl) { return 0; } + +static inline int reset_assert_all(struct reset_ctl *reset_ctl, int count) +{ + return 0; +} + #endif
#endif

From: Patrice Chotard patrice.chotard@st.com
Add clk_count() method to be able to get the number of clocks contained into a clock property. This will allow to allocate the right amount of memory in order to keep clock reference. These clock reference can be used later on error path or in .remove callback to release these clocks.
Signed-off-by: Patrice Chotard patrice.chotard@st.com ---
v4: _ add clk_count() method
drivers/clk/clk-uclass.c | 12 ++++++++++++ include/clk.h | 12 ++++++++++++ 2 files changed, 24 insertions(+)
diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index 6fcfd69..5c4dd19 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -96,8 +96,20 @@ int clk_get_by_index(struct udevice *dev, int index, struct clk *clk)
return clk_request(dev_clk, clk); } + # endif /* OF_PLATDATA */
+int clk_count(struct udevice *dev) +{ + int count; + struct clk clk; + + for (count = 0; ; count++) { + if (clk_get_by_index(dev, count, &clk)) + return count; + } +} + int clk_get_by_name(struct udevice *dev, const char *name, struct clk *clk) { int index; diff --git a/include/clk.h b/include/clk.h index 5a5c2ff..801920c 100644 --- a/include/clk.h +++ b/include/clk.h @@ -98,6 +98,18 @@ int clk_get_by_index(struct udevice *dev, int index, struct clk *clk); * @return 0 if OK, or a negative error code. */ int clk_get_by_name(struct udevice *dev, const char *name, struct clk *clk); + +/** + * clk_count - Get clock count contained in the "clocks" property. + * + * This returns the count of clock found into the "clocks" property. This + * allows to allocate the right amount of memory to keep clock reference. + * + * @dev: The client device. + * @return number of clocks found. + */ +int clk_count(struct udevice *dev); + #else static inline int clk_get_by_index(struct udevice *dev, int index, struct clk *clk)

From: Patrice Chotard patrice.chotard@st.com
Add clk_disable_all() method which Request/Disable/Free an array of clocks that has been previously requested by clk_request/get_by_*()
Signed-off-by: Patrice Chotard patrice.chotard@st.com ---
v4: _ add clk_disable_all() method as suggested by Marek Vasut and Simon Glass
drivers/clk/clk-uclass.c | 22 ++++++++++++++++++++++ include/clk.h | 10 ++++++++++ 2 files changed, 32 insertions(+)
diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index 5c4dd19..44bac0d 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -201,6 +201,28 @@ int clk_disable(struct clk *clk) return ops->disable(clk); }
+int clk_disable_all(struct clk *clk, int count) +{ + int i, ret; + + debug("%s(clk=%p count=%d)\n", __func__, clk, count); + + for (i = 0; i < count; i++) { + ret = clk_request(clk->dev, &clk[i]); + if (ret && ret != -ENOSYS) + return ret; + + ret = clk_disable(&clk[i]); + if (ret && ret != -ENOSYS) + return ret; + + ret = clk_free(&clk[i]); + if (ret && ret != -ENOSYS) + return ret; + } + return 0; +} + UCLASS_DRIVER(clk) = { .id = UCLASS_CLK, .name = "clk", diff --git a/include/clk.h b/include/clk.h index 801920c..25a3f32 100644 --- a/include/clk.h +++ b/include/clk.h @@ -186,6 +186,16 @@ int clk_enable(struct clk *clk); */ int clk_disable(struct clk *clk);
+/** + * clk_disable_all() - Request/Disable (turn off)/Free clocks. + * + * @clk: A clock struct array that was previously successfully + * requested by clk_request/get_by_*(). + * @count Number of clock contained in the array + * @return zero on success, or -ve error code. + */ +int clk_disable_all(struct clk *clk, int count); + int soc_clk_dump(void);
#endif

On 24 May 2017 at 07:07, patrice.chotard@st.com wrote:
From: Patrice Chotard patrice.chotard@st.com
Add clk_disable_all() method which Request/Disable/Free an array of clocks that has been previously requested by clk_request/get_by_*()
Signed-off-by: Patrice Chotard patrice.chotard@st.com
v4: _ add clk_disable_all() method as suggested by Marek Vasut and Simon Glass
drivers/clk/clk-uclass.c | 22 ++++++++++++++++++++++ include/clk.h | 10 ++++++++++ 2 files changed, 32 insertions(+)
Reviewed-by: Simon Glass sjg@chromium.org

From: Patrice Chotard patrice.chotard@st.com
This allows to get file, line and function location of the current error message.
Signed-off-by: Patrice Chotard patrice.chotard@st.com Reviewed-by: Simon Glass sjg@chromium.org ---
v4: _ none
v3: _ add commit message
v2: _ create this independant path for printf() replacement
drivers/usb/host/ehci-generic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/host/ehci-generic.c b/drivers/usb/host/ehci-generic.c index 2190adb..6058e9a 100644 --- a/drivers/usb/host/ehci-generic.c +++ b/drivers/usb/host/ehci-generic.c @@ -34,7 +34,7 @@ static int ehci_usb_probe(struct udevice *dev) if (ret < 0) break; if (clk_enable(&clk)) - printf("failed to enable clock %d\n", i); + error("failed to enable clock %d\n", i); clk_free(&clk); }
@@ -46,7 +46,7 @@ static int ehci_usb_probe(struct udevice *dev) if (ret < 0) break; if (reset_deassert(&reset)) - printf("failed to deassert reset %d\n", i); + error("failed to deassert reset %d\n", i); reset_free(&reset); }

From: Patrice Chotard patrice.chotard@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@st.com ---
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(),v4 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 | 114 +++++++++++++++++++++++++++++++--------- 1 file changed, 90 insertions(+), 24 deletions(-)
diff --git a/drivers/usb/host/ehci-generic.c b/drivers/usb/host/ehci-generic.c index 6058e9a..354a882 100644 --- a/drivers/usb/host/ehci-generic.c +++ b/drivers/usb/host/ehci-generic.c @@ -18,43 +18,109 @@ */ 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 = clk_count(dev); + + if (clock_nb) { + priv->clocks = devm_kmalloc(dev, sizeof(struct clk) * clock_nb, + GFP_KERNEL); + if (!priv->clocks) { + error("Can't allocate resource\n"); + return -ENOMEM; + } + + for (i = 0; i < clock_nb; i++) { + err = clk_get_by_index(dev, i, &priv->clocks[i]); + + if (err < 0) + break;
- for (i = 0; ; i++) { - struct reset_ctl reset; - int ret; + priv->clock_count++;
- 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 (clk_enable(&priv->clocks[i])) { + error("failed to enable clock %d\n", i); + clk_free(&priv->clocks[i]); + goto clk_err; + } + clk_free(&priv->clocks[i]); + } }
+ priv->reset_count = 0; + reset_nb = reset_count(dev); + + if (reset_nb) { + priv->resets = devm_kmalloc(dev, + sizeof(struct reset_ctl) * reset_nb, + GFP_KERNEL); + if (!priv->resets) { + error("Can't allocate resource\n"); + return -ENOMEM; + } + + for (i = 0; i < reset_nb; i++) { + err = reset_get_by_index(dev, i, &priv->resets[i]); + if (err < 0) + break; + + priv->reset_count++; + + if (reset_deassert(&priv->resets[i])) { + error("failed to deassert reset %d\n", i); + reset_free(&priv->resets[i]); + goto reset_err; + } + reset_free(&priv->resets[i]); + } + } hccr = map_physmem(dev_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) + return err; + +reset_err: + ret = reset_assert_all(priv->resets, priv->reset_count); + if (ret) + return ret; +clk_err: + ret = clk_disable_all(priv->clocks, priv->clock_count); + if (ret) + return ret; + + 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_assert_all(priv->resets, priv->reset_count); + if (ret) + return ret; + + return clk_disable_all(priv->clocks, priv->clock_count); }
static const struct udevice_id ehci_usb_ids[] = { @@ -67,7 +133,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,

From: Patrice Chotard patrice.chotard@st.com
Extend ehci-generic driver with generic PHY framework
Signed-off-by: Patrice Chotard patrice.chotard@st.com ---
v4: _ use generic_phy_valid() before generic_phy_exit() call
v3: _ test return value on generic_phy_get_by_index() and generic_phy_init()
drivers/usb/host/ehci-generic.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+)
diff --git a/drivers/usb/host/ehci-generic.c b/drivers/usb/host/ehci-generic.c index 354a882..0b0ddc1 100644 --- a/drivers/usb/host/ehci-generic.c +++ b/drivers/usb/host/ehci-generic.c @@ -6,6 +6,8 @@
#include <common.h> #include <clk.h> +#include <fdtdec.h> +#include <generic-phy.h> #include <reset.h> #include <asm/io.h> #include <dm.h> @@ -20,6 +22,7 @@ struct generic_ehci { struct ehci_ctrl ctrl; struct clk *clocks; struct reset_ctl *resets; + struct phy phy; int clock_count; int reset_count; }; @@ -87,6 +90,21 @@ static int ehci_usb_probe(struct udevice *dev) reset_free(&priv->resets[i]); } } + + err = generic_phy_get_by_index(dev, 0, &priv->phy); + if (err) { + if (err != -ENOENT) { + error("failed to get usb phy\n"); + goto reset_err; + } + } else { + err = generic_phy_init(&priv->phy); + if (err) { + error("failed to init usb phy\n"); + goto reset_err; + } + } + hccr = map_physmem(dev_get_addr(dev), 0x100, MAP_NOCACHE); hcor = (struct ehci_hcor *)((uintptr_t)hccr + HC_LENGTH(ehci_readl(&hccr->cr_capbase))); @@ -95,6 +113,12 @@ static int ehci_usb_probe(struct udevice *dev) if (!err) return err;
+ if (generic_phy_valid(&priv->phy)) { + ret = generic_phy_exit(&priv->phy); + if (ret) + return ret; + } + reset_err: ret = reset_assert_all(priv->resets, priv->reset_count); if (ret) @@ -116,6 +140,12 @@ static int ehci_usb_remove(struct udevice *dev) if (ret) return ret;
+ if (generic_phy_valid(&priv->phy)) { + ret = generic_phy_exit(&priv->phy); + if (ret) + return ret; + } + ret = reset_assert_all(priv->resets, priv->reset_count); if (ret) return ret;

From: Patrice Chotard patrice.chotard@st.com
use array to save enabled clocks reference in order to disabled them in case of error during probe() or during driver removal.
Signed-off-by: Patrice Chotard patrice.chotard@st.com ---
v4: _ use generic_phy_valid() before generic_phy_exit() call
v3: _ extract in this patch the CLOCK support add-on from previous patch 5 _ keep enabled clocks reference in list in order to disable clocks in error path or in .remove callback
v2: _ add error path management _ add .remove callback
drivers/usb/host/ohci-generic.c | 53 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/host/ohci-generic.c b/drivers/usb/host/ohci-generic.c index f3307f4..404fc98 100644 --- a/drivers/usb/host/ohci-generic.c +++ b/drivers/usb/host/ohci-generic.c @@ -5,6 +5,7 @@ */
#include <common.h> +#include <clk.h> #include <dm.h> #include "ohci.h"
@@ -14,18 +15,66 @@
struct generic_ohci { ohci_t ohci; + struct clk *clocks; + int clock_count; };
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, err, ret, clock_nb;
- return ohci_register(dev, regs); + err = 0; + priv->clock_count = 0; + clock_nb = clk_count(dev); + + if (clock_nb) { + priv->clocks = devm_kmalloc(dev, sizeof(struct clk) * clock_nb, + GFP_KERNEL); + if (!priv->clocks) { + error("Can't allocate resource\n"); + return -ENOMEM; + } + + for (i = 0; i < clock_nb; i++) { + err = clk_get_by_index(dev, i, &priv->clocks[i]); + if (err < 0) + break; + + priv->clock_count++; + + if (clk_enable(&priv->clocks[i])) { + error("failed to enable clock %d\n", i); + clk_free(&priv->clocks[i]); + goto clk_err; + } + clk_free(&priv->clocks[i]); + } + } + + err = ohci_register(dev, regs); + if (!err) + return err; + +clk_err: + ret = clk_disable_all(priv->clocks, priv->clock_count); + if (ret) + return ret; + + return err; }
static int ohci_usb_remove(struct udevice *dev) { - return ohci_deregister(dev); + struct generic_ohci *priv = dev_get_priv(dev); + int ret; + + ret = ohci_deregister(dev); + if (ret) + return ret; + + return clk_disable_all(priv->clocks, priv->clock_count); }
static const struct udevice_id ohci_usb_ids[] = {

From: Patrice Chotard patrice.chotard@st.com
use array to save deasserted resets reference in order to assert them in case of error during probe() or during driver removal.
Signed-off-by: Patrice Chotard patrice.chotard@st.com ---
v4: _ update the memory allocation for deasserted resets. Replace lists by arrays. _ usage of new RESET methods reset_assert_all() and clk_disable_all().
v3: _ extract in this patch the RESET support add-on from previous patch 5 _ keep deasserted 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 | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/host/ohci-generic.c b/drivers/usb/host/ohci-generic.c index 404fc98..3f41015 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) @@ -16,14 +17,16 @@ struct generic_ohci { ohci_t ohci; struct clk *clocks; + struct reset_ctl *resets; int clock_count; + int reset_count; };
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, err, ret, clock_nb; + int i, err, ret, clock_nb, reset_nb;
err = 0; priv->clock_count = 0; @@ -53,10 +56,42 @@ static int ohci_usb_probe(struct udevice *dev) } }
+ priv->reset_count = 0; + reset_nb = reset_count(dev); + + if (reset_nb) { + priv->resets = devm_kmalloc(dev, + sizeof(struct reset_ctl) * reset_nb, + GFP_KERNEL); + if (!priv->resets) { + error("Can't allocate resource\n"); + return -ENOMEM; + } + + for (i = 0; i < reset_nb; i++) { + err = reset_get_by_index(dev, i, &priv->resets[i]); + if (err < 0) + break; + + priv->reset_count++; + + if (reset_deassert(&priv->resets[i])) { + error("failed to deassert reset %d\n", i); + reset_free(&priv->resets[i]); + goto reset_err; + } + reset_free(&priv->resets[i]); + } + } + err = ohci_register(dev, regs); if (!err) return err;
+reset_err: + ret = reset_assert_all(priv->resets, priv->reset_count); + if (ret) + return ret; clk_err: ret = clk_disable_all(priv->clocks, priv->clock_count); if (ret) @@ -74,6 +109,10 @@ static int ohci_usb_remove(struct udevice *dev) if (ret) return ret;
+ ret = reset_assert_all(priv->resets, priv->reset_count); + if (ret) + return ret; + return clk_disable_all(priv->clocks, priv->clock_count); }

From: Patrice Chotard patrice.chotard@st.com
Extend ohci-generic driver with generic PHY framework
Signed-off-by: Patrice Chotard patrice.chotard@st.com ---
v4: _ use generic_phy_valid() before generic_phy_exit() call
v3: _ extract in this patch the PHY support add-on from previous patch 5
drivers/usb/host/ohci-generic.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+)
diff --git a/drivers/usb/host/ohci-generic.c b/drivers/usb/host/ohci-generic.c index 3f41015..b028465 100644 --- a/drivers/usb/host/ohci-generic.c +++ b/drivers/usb/host/ohci-generic.c @@ -6,6 +6,8 @@
#include <common.h> #include <clk.h> +#include <fdtdec.h> +#include <generic-phy.h> #include <dm.h> #include <reset.h> #include "ohci.h" @@ -18,6 +20,7 @@ struct generic_ohci { ohci_t ohci; struct clk *clocks; struct reset_ctl *resets; + struct phy phy; int clock_count; int reset_count; }; @@ -84,10 +87,30 @@ static int ohci_usb_probe(struct udevice *dev) } }
+ err = generic_phy_get_by_index(dev, 0, &priv->phy); + if (err) { + if (err != -ENOENT) { + error("failed to get usb phy\n"); + goto reset_err; + } + } else { + err = generic_phy_init(&priv->phy); + if (err) { + error("failed to init usb phy\n"); + goto reset_err; + } + } + err = ohci_register(dev, regs); if (!err) return err;
+ if (generic_phy_valid(&priv->phy)) { + ret = generic_phy_exit(&priv->phy); + if (ret) + return ret; + } + reset_err: ret = reset_assert_all(priv->resets, priv->reset_count); if (ret) @@ -109,6 +132,12 @@ static int ohci_usb_remove(struct udevice *dev) if (ret) return ret;
+ if (generic_phy_valid(&priv->phy)) { + ret = generic_phy_exit(&priv->phy); + if (ret) + return ret; + } + ret = reset_assert_all(priv->resets, priv->reset_count); if (ret) return ret;
participants (2)
-
patrice.chotard@st.com
-
Simon Glass