Re: [PATCH] Fix OMAP EHCI suspend/resume failure (i693)

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux