[PATCH 1/1] ehci-mxc: Fix mx31 OTG host initialisation

Valentin Longchamp valentin.longchamp at epfl.ch
Tue May 11 05:03:41 EDT 2010


See the comments below.

On 05/11/2010 08:39 AM, Sascha Hauer wrote:
> On Mon, May 10, 2010 at 08:13:40PM +0200, Philippe Rétornaz wrote:
>> On mx31 the OTG host initialisation fail if you need to have
>> an ULPI transfert to initialize the PHY.
>>
>> In order to be able to communicate with the PHY a complete reset
>> of the usb host is needed. After the PHY initialization the host
>> usb configuration registers need to be rewritten to avoid a host
>> controller lockup.
>>
>> Signed-off-by: Philippe Rétornaz<philippe.retornaz at epfl.ch>
>> ---
>>   drivers/usb/host/ehci-mxc.c |   68 +++++++++++++++++++++++++++++++++++++++++++
>>   1 files changed, 68 insertions(+), 0 deletions(-)
>>
>> diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c
>> index 544ccfd..39d28da 100644
>> --- a/drivers/usb/host/ehci-mxc.c
>> +++ b/drivers/usb/host/ehci-mxc.c
>> @@ -29,6 +29,11 @@
>>   #define PORTSC_OFFSET		0x184
>>   #define USBMODE_OFFSET		0x1a8
>>   #define USBMODE_CM_HOST		3
>> +#define USBCMD_OFFSET		0x140
>> +#define USBCMD_RS		(1<<  0)
>> +#define USBCMD_RST		(1<<  1)
>> +#define USBSTS_OFFSET		0x144
>> +#define USBSTS_HCH		(1<<  12)
>>
>>   struct ehci_mxc_priv {
>>   	struct clk *usbclk, *ahbclk;
>> @@ -120,6 +125,7 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev)
>>   	int irq, ret, temp;
>>   	struct ehci_mxc_priv *priv;
>>   	struct device *dev =&pdev->dev;
>> +	int i;
>>
>>   	dev_info(&pdev->dev, "initializing i.MX USB Controller\n");
>>
>> @@ -204,6 +210,51 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev)
>>   	if (ret<  0)
>>   		goto err_init;
>>
>> +	/* i.Mx31 OTG host has a bug, if you don't do a reset, then ULPI
>> +	 * transfert timeout. */
>
> s/transfert/transfers/
> Same below.
>
>> +	if (cpu_is_mx31()&&  pdev->id == 0) {
>> +		/* Wait for the controller to go idle */
>> +		for (i = 0; i<  10000; i++) {
>> +			if (readl(hcd->regs + USBSTS_OFFSET)&  USBSTS_HCH)
>> +				break;
>> +			udelay(1);
>> +		}
>> +		if (i == 10000) {
>> +			dev_err(dev, "Timeout while stopping USB controller\n");
>> +			goto err_init;
>> +		}
>> +
>> +		/* Stop the usb controller */
>> +		temp = readl(hcd->regs + USBCMD_OFFSET);
>> +		writel(temp&  (~USBCMD_RS), hcd->regs + USBCMD_OFFSET);
>> +
>> +		for (i = 0; i<  10000; i++) {
>> +			if (!(readl(hcd->regs + USBCMD_OFFSET)&  USBCMD_RS))
>> +				break;
>> +			udelay(1);
>> +		}
>> +
>> +		if (i == 10000) {
>> +			dev_err(dev, "Timeout while stopping USB controller\n");
>> +			goto err_init;
>> +		}
>> +
>> +		/* Reset the usb controller */
>> +		temp = readl(hcd->regs + USBCMD_OFFSET);
>> +		writel(temp | USBCMD_RST, hcd->regs + USBCMD_OFFSET);
>> +
>> +		for (i = 0; i<  10000; i++) {
>> +			if (!(readl(hcd->regs + USBCMD_OFFSET)&  USBCMD_RST))
>> +				break;
>> +			udelay(1);
>> +		}
>> +
>> +		if (i == 10000) {
>> +			dev_err(dev, "Timeout while reseting USB controller\n");
>> +			goto err_init;
>> +		}
>> +	}
>
> You add the resetting of the controller after setting up USBMODE/PORTSC
> setup. Wouldn't it be possible to change the order so that we do not
> have to do it twice?

It has to be tested, but if it works the code would then be clearer.

> Also, I think the whole reset functionality should be a seperate
> function. I can imagine we'll need it elsewhere or on different SoCs in
> which case the if clause above gets complicated.

I also think most of the things could be implemented as functions for a 
better readability (reset, and as Mark suggested a static inline 
function for the delay).

Val

>
> Sascha
>
>
>> +
>>   	/* Initialize the transceiver */
>>   	if (pdata->otg) {
>>   		pdata->otg->io_priv = hcd->regs + ULPI_VIEWPORT_OFFSET;
>> @@ -213,6 +264,23 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev)
>>   			dev_err(dev, "unable to enable vbus on transceiver\n");
>>   	}
>>
>> +	/* i.Mx31 OTG host has a bug, if you do an ULPI transfert then the host
>> +	 * controller stay busy. Rewriting the register is enough to make it
>> +	 * working */
>> +	if (cpu_is_mx31()&&  pdev->id == 0) {
>> +		/* set USBMODE to host mode */
>> +		temp = readl(hcd->regs + USBMODE_OFFSET);
>> +		writel(temp | USBMODE_CM_HOST, hcd->regs + USBMODE_OFFSET);
>> +
>> +		/* set up the PORTSCx register */
>> +		writel(pdata->portsc, hcd->regs + PORTSC_OFFSET);
>> +
>> +		/* setup USBCONTROL. */
>> +		ret = mxc_initialize_usb_hw(pdev->id, pdata->flags);
>> +		if (ret<  0)
>> +			goto err_init;
>> +	}
>> +
>>   	priv->hcd = hcd;
>>   	platform_set_drvdata(pdev, priv);
>>
>> --
>> 1.6.3.3
>>
>>
>


-- 
Valentin Longchamp, PhD Student, EPFL-STI-LSRO1
valentin.longchamp at epfl.ch, Phone: +41216937827
http://people.epfl.ch/valentin.longchamp
MEB3494, Station 9, CH-1015 Lausanne



More information about the linux-arm-kernel mailing list