Message ID | 20240502-msm8916-hs-usb-clocks-v1-2-eeccf483b68d@samcday.com |
---|---|
State | Superseded |
Delegated to: | Caleb Connolly |
Headers | show |
Series | qcom: ehci: enable core + iface clocks | expand |
On 02/05/2024 15:16, Sam Day wrote: > This seems to be necessary on my samsung-a5. Without this patch, the > first access of EHCI registers causes a bus stall and subsequent reset. > > I am unsure why this wasn't already necessary for db410c, perhaps those > clocks are already enabled on boot. > > Signed-off-by: Sam Day <me@samcday.com> > --- > drivers/usb/host/ehci-msm.c | 37 +++++++++++++++++++++++++++++++++++-- > 1 file changed, 35 insertions(+), 2 deletions(-) > > diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c > index 98fe7bc3bc..b2e294dd64 100644 > --- a/drivers/usb/host/ehci-msm.c > +++ b/drivers/usb/host/ehci-msm.c > @@ -7,8 +7,10 @@ > * Based on Linux driver > */ > > +#include <clk.h> > #include <common.h> > #include <dm.h> > +#include <dm/device_compat.h> > #include <dm/lists.h> > #include <errno.h> > #include <usb.h> > @@ -25,6 +27,8 @@ struct msm_ehci_priv { > struct usb_ehci *ehci; /* Start of IP core*/ > struct ulpi_viewport ulpi_vp; /* ULPI Viewport */ > struct phy phy; > + struct clk iface_clk; > + struct clk core_clk; You could simplify this with the bulk clock API, but I'm easy either way. Reviewed-by: Caleb Connolly <caleb.connolly@linaro.org> > }; > > static int msm_init_after_reset(struct ehci_ctrl *dev) > @@ -53,20 +57,46 @@ static int ehci_usb_probe(struct udevice *dev) > struct ehci_hcor *hcor; > int ret; > > + ret = clk_get_by_name(dev, "core", &p->core_clk); > + if (ret) { > + dev_err(dev, "Failed to get core clock: %d\n", ret); > + return ret; > + } > + > + ret = clk_get_by_name(dev, "iface", &p->iface_clk); > + if (ret) { > + dev_err(dev, "Failed to get iface clock: %d\n", ret); > + return ret; > + } > + > + ret = clk_prepare_enable(&p->core_clk); > + if (ret) > + return ret; > + > + ret = clk_prepare_enable(&p->iface_clk); > + if (ret) > + goto cleanup_core; > + > hccr = (struct ehci_hccr *)((phys_addr_t)&ehci->caplength); > hcor = (struct ehci_hcor *)((phys_addr_t)hccr + > HC_LENGTH(ehci_readl(&(hccr)->cr_capbase))); > > ret = generic_setup_phy(dev, &p->phy, 0); > if (ret) > - return ret; > + goto cleanup_iface; > > ret = board_usb_init(0, plat->init_type); > if (ret < 0) > - return ret; > + goto cleanup_iface; > > return ehci_register(dev, hccr, hcor, &msm_ehci_ops, 0, > plat->init_type); > + > +cleanup_iface: > + clk_disable_unprepare(&p->iface_clk); > +cleanup_core: > + clk_disable_unprepare(&p->core_clk); > + return ret; > } > > static int ehci_usb_remove(struct udevice *dev) > @@ -82,6 +112,9 @@ static int ehci_usb_remove(struct udevice *dev) > /* Stop controller. */ > clrbits_le32(&ehci->usbcmd, CMD_RUN); > > + clk_disable_unprepare(&p->iface_clk); > + clk_disable_unprepare(&p->core_clk); > + > ret = generic_shutdown_phy(&p->phy); > if (ret) > return ret; >
diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c index 98fe7bc3bc..b2e294dd64 100644 --- a/drivers/usb/host/ehci-msm.c +++ b/drivers/usb/host/ehci-msm.c @@ -7,8 +7,10 @@ * Based on Linux driver */ +#include <clk.h> #include <common.h> #include <dm.h> +#include <dm/device_compat.h> #include <dm/lists.h> #include <errno.h> #include <usb.h> @@ -25,6 +27,8 @@ struct msm_ehci_priv { struct usb_ehci *ehci; /* Start of IP core*/ struct ulpi_viewport ulpi_vp; /* ULPI Viewport */ struct phy phy; + struct clk iface_clk; + struct clk core_clk; }; static int msm_init_after_reset(struct ehci_ctrl *dev) @@ -53,20 +57,46 @@ static int ehci_usb_probe(struct udevice *dev) struct ehci_hcor *hcor; int ret; + ret = clk_get_by_name(dev, "core", &p->core_clk); + if (ret) { + dev_err(dev, "Failed to get core clock: %d\n", ret); + return ret; + } + + ret = clk_get_by_name(dev, "iface", &p->iface_clk); + if (ret) { + dev_err(dev, "Failed to get iface clock: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(&p->core_clk); + if (ret) + return ret; + + ret = clk_prepare_enable(&p->iface_clk); + if (ret) + goto cleanup_core; + hccr = (struct ehci_hccr *)((phys_addr_t)&ehci->caplength); hcor = (struct ehci_hcor *)((phys_addr_t)hccr + HC_LENGTH(ehci_readl(&(hccr)->cr_capbase))); ret = generic_setup_phy(dev, &p->phy, 0); if (ret) - return ret; + goto cleanup_iface; ret = board_usb_init(0, plat->init_type); if (ret < 0) - return ret; + goto cleanup_iface; return ehci_register(dev, hccr, hcor, &msm_ehci_ops, 0, plat->init_type); + +cleanup_iface: + clk_disable_unprepare(&p->iface_clk); +cleanup_core: + clk_disable_unprepare(&p->core_clk); + return ret; } static int ehci_usb_remove(struct udevice *dev) @@ -82,6 +112,9 @@ static int ehci_usb_remove(struct udevice *dev) /* Stop controller. */ clrbits_le32(&ehci->usbcmd, CMD_RUN); + clk_disable_unprepare(&p->iface_clk); + clk_disable_unprepare(&p->core_clk); + ret = generic_shutdown_phy(&p->phy); if (ret) return ret;
This seems to be necessary on my samsung-a5. Without this patch, the first access of EHCI registers causes a bus stall and subsequent reset. I am unsure why this wasn't already necessary for db410c, perhaps those clocks are already enabled on boot. Signed-off-by: Sam Day <me@samcday.com> --- drivers/usb/host/ehci-msm.c | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-)