On Tue, Jun 05, 2012 at 03:34:27PM +0300, Volodymyr Mieshkov wrote: > From: Anand Gadiyar <gadiyar@xxxxxx> > > Its observed with some PHY, the 60Mhz clock gets > cut too soon for OMAP EHCI, leaving OMAP-EHCI in a bad state. > > So on starting port suspend, make sure the 60Mhz clock to EHCI > is kept alive using an internal clock, so that EHCi can cleanly > transition its hw state machine on a port suspend. > > Its not proven if this is the issue hit on USB3333, > but the symptoms look very similar. > > Signed-off-by: Anand Gadiyar <gadiyar@xxxxxx> > Signed-off-by: Vikram Pandita <vikram.pandita@xxxxxx> > Signed-off-by: Volodymyr Mieshkov <x0182794@xxxxxx> looks good to me: Acked-by: Felipe Balbi <balbi@xxxxxx> > diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c > index 5c78f9e..cb0de0e 100644 > --- a/drivers/usb/host/ehci-omap.c > +++ b/drivers/usb/host/ehci-omap.c > @@ -43,6 +43,7 @@ > #include <linux/regulator/consumer.h> > #include <linux/pm_runtime.h> > #include <linux/gpio.h> > +#include <linux/clk.h> > > /* EHCI Register Set */ > #define EHCI_INSNREG04 (0xA0) > @@ -55,6 +56,15 @@ > #define EHCI_INSNREG05_ULPI_EXTREGADD_SHIFT 8 > #define EHCI_INSNREG05_ULPI_WRDATA_SHIFT 0 > > +/* Errata i693 */ > +static struct clk *utmi_p1_fck; > +static struct clk *utmi_p2_fck; > +static struct clk *xclk60mhsp1_ck; > +static struct clk *xclk60mhsp2_ck; > +static struct clk *usbhost_p1_fck; > +static struct clk *usbhost_p2_fck; > +static struct clk *init_60m_fclk; > + > /*-------------------------------------------------------------------------*/ > > static const struct hc_driver ehci_omap_hc_driver; > @@ -70,6 +80,41 @@ static inline u32 ehci_read(void __iomem *base, u32 reg) > return __raw_readl(base + reg); > } > > +/* Erratum i693 workaround sequence */ > +static void omap_ehci_erratum_i693(struct ehci_hcd *ehci) > +{ > + int ret = 0; > + > + /* Switch to the internal 60 MHz clock */ > + ret = clk_set_parent(utmi_p1_fck, init_60m_fclk); > + if (ret != 0) > + ehci_err(ehci, "init_60m_fclk set parent" > + "failed error:%d\n", ret); > + > + ret = clk_set_parent(utmi_p2_fck, init_60m_fclk); > + if (ret != 0) > + ehci_err(ehci, "init_60m_fclk set parent" > + "failed error:%d\n", ret); > + > + clk_enable(usbhost_p1_fck); > + clk_enable(usbhost_p2_fck); > + > + /* Wait 1ms and switch back to the external clock */ > + mdelay(1); > + ret = clk_set_parent(utmi_p1_fck, xclk60mhsp1_ck); > + if (ret != 0) > + ehci_err(ehci, "xclk60mhsp1_ck set parent" > + "failed error:%d\n", ret); > + > + ret = clk_set_parent(utmi_p2_fck, xclk60mhsp2_ck); > + if (ret != 0) > + ehci_err(ehci, "xclk60mhsp2_ck set parent" > + "failed error:%d\n", ret); > + > + clk_disable(usbhost_p1_fck); > + clk_disable(usbhost_p2_fck); > +} > + > static void omap_ehci_soft_phy_reset(struct platform_device *pdev, u8 port) > { > struct usb_hcd *hcd = dev_get_drvdata(&pdev->dev); > @@ -100,6 +145,50 @@ static void omap_ehci_soft_phy_reset(struct platform_device *pdev, u8 port) > } > } > > +static int omap_ehci_hub_control( > + struct usb_hcd *hcd, > + u16 typeReq, > + u16 wValue, > + u16 wIndex, > + char *buf, > + u16 wLength > +) > +{ > + struct ehci_hcd *ehci = hcd_to_ehci(hcd); > + u32 __iomem *status_reg = &ehci->regs->port_status[ > + (wIndex & 0xff) - 1]; > + u32 temp; > + unsigned long flags; > + int retval = 0; > + > + spin_lock_irqsave(&ehci->lock, flags); > + > + if (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_SUSPEND) { > + temp = ehci_readl(ehci, status_reg); > + if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) != 0) { > + retval = -EPIPE; > + goto done; > + } > + > + temp &= ~PORT_WKCONN_E; > + temp |= PORT_WKDISC_E | PORT_WKOC_E; > + ehci_writel(ehci, temp | PORT_SUSPEND, status_reg); > + > + omap_ehci_erratum_i693(ehci); > + > + set_bit((wIndex & 0xff) - 1, &ehci->suspended_ports); > + goto done; > + } > + > + spin_unlock_irqrestore(&ehci->lock, flags); > + > + /* Handle the hub control events here */ > + return ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength); > +done: > + spin_unlock_irqrestore(&ehci->lock, flags); > + return retval; > +} > + > static void disable_put_regulator( > struct ehci_hcd_omap_platform_data *pdata) > { > @@ -264,8 +353,76 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) > gpio_set_value(pdata->reset_gpio_port[1], 1); > } > > + /* get clocks */ > + utmi_p1_fck = clk_get(dev, "utmi_p1_gfclk"); > + if (IS_ERR(utmi_p1_fck)) { > + ret = PTR_ERR(utmi_p1_fck); > + dev_err(dev, "utmi_p1_gfclk failed error:%d\n", ret); > + goto err_add_hcd; > + } > + > + xclk60mhsp1_ck = clk_get(dev, "xclk60mhsp1_ck"); > + if (IS_ERR(xclk60mhsp1_ck)) { > + ret = PTR_ERR(xclk60mhsp1_ck); > + dev_err(dev, "xclk60mhsp1_ck failed error:%d\n", ret); > + goto err_utmi_p1_fck; > + } > + > + utmi_p2_fck = clk_get(dev, "utmi_p2_gfclk"); > + if (IS_ERR(utmi_p2_fck)) { > + ret = PTR_ERR(utmi_p2_fck); > + dev_err(dev, "utmi_p2_gfclk failed error:%d\n", ret); > + goto err_xclk60mhsp1_ck; > + } > + > + xclk60mhsp2_ck = clk_get(dev, "xclk60mhsp2_ck"); > + if (IS_ERR(xclk60mhsp2_ck)) { > + ret = PTR_ERR(xclk60mhsp2_ck); > + dev_err(dev, "xclk60mhsp2_ck failed error:%d\n", ret); > + goto err_utmi_p2_fck; > + } > + > + usbhost_p1_fck = clk_get(dev, "usb_host_hs_utmi_p1_clk"); > + if (IS_ERR(usbhost_p1_fck)) { > + ret = PTR_ERR(usbhost_p1_fck); > + dev_err(dev, "usbhost_p1_fck failed error:%d\n", ret); > + goto err_xclk60mhsp2_ck; > + } > + > + usbhost_p2_fck = clk_get(dev, "usb_host_hs_utmi_p2_clk"); > + if (IS_ERR(usbhost_p2_fck)) { > + ret = PTR_ERR(usbhost_p2_fck); > + dev_err(dev, "usbhost_p2_fck failed error:%d\n", ret); > + goto err_usbhost_p1_fck; > + } > + > + init_60m_fclk = clk_get(dev, "init_60m_fclk"); > + if (IS_ERR(init_60m_fclk)) { > + ret = PTR_ERR(init_60m_fclk); > + dev_err(dev, "init_60m_fclk failed error:%d\n", ret); > + goto err_usbhost_p2_fck; > + } > + > return 0; > > +err_usbhost_p2_fck: > + clk_put(usbhost_p2_fck); > + > +err_usbhost_p1_fck: > + clk_put(usbhost_p1_fck); > + > +err_xclk60mhsp2_ck: > + clk_put(xclk60mhsp2_ck); > + > +err_utmi_p2_fck: > + clk_put(utmi_p2_fck); > + > +err_xclk60mhsp1_ck: > + clk_put(xclk60mhsp1_ck); > + > +err_utmi_p1_fck: > + clk_put(utmi_p1_fck); > + > err_add_hcd: > disable_put_regulator(pdata); > pm_runtime_put_sync(dev); > @@ -294,6 +451,15 @@ static int ehci_hcd_omap_remove(struct platform_device *pdev) > disable_put_regulator(dev->platform_data); > iounmap(hcd->regs); > usb_put_hcd(hcd); > + > + clk_put(utmi_p1_fck); > + clk_put(utmi_p2_fck); > + clk_put(xclk60mhsp1_ck); > + clk_put(xclk60mhsp2_ck); > + clk_put(usbhost_p1_fck); > + clk_put(usbhost_p2_fck); > + clk_put(init_60m_fclk); > + > pm_runtime_put_sync(dev); > pm_runtime_disable(dev); > > @@ -364,7 +530,7 @@ static const struct hc_driver ehci_omap_hc_driver = { > * root hub support > */ > .hub_status_data = ehci_hub_status_data, > - .hub_control = ehci_hub_control, > + .hub_control = omap_ehci_hub_control, > .bus_suspend = ehci_bus_suspend, > .bus_resume = ehci_bus_resume, > > -- > 1.7.0.4 -- balbi
Attachment:
signature.asc
Description: Digital signature