diff mbox

[U-Boot,RFC,05/11] ehci: Add support for Qualcomm EHCI

Message ID 1449783707-23594-6-git-send-email-mateusz.kulikowski@gmail.com
State RFC
Delegated to: Tom Rini
Headers show

Commit Message

Mateusz Kulikowski Dec. 10, 2015, 9:41 p.m. UTC
This driver is able to reconfigure OTG controller into HOST mode.
Board can add board-specific initialization as board_prepare_usb().
It requires USB_ULPI_VIEWPORT enabled in board configuration.

Signed-off-by: Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
---

 drivers/usb/host/Kconfig    |   8 ++
 drivers/usb/host/Makefile   |   1 +
 drivers/usb/host/ehci-msm.c | 198 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 207 insertions(+)
 create mode 100644 drivers/usb/host/ehci-msm.c

Comments

Marek Vasut Dec. 10, 2015, 11:22 p.m. UTC | #1
On Thursday, December 10, 2015 at 10:41:41 PM, Mateusz Kulikowski wrote:
> This driver is able to reconfigure OTG controller into HOST mode.
> Board can add board-specific initialization as board_prepare_usb().
> It requires USB_ULPI_VIEWPORT enabled in board configuration.
> 
> Signed-off-by: Mateusz Kulikowski <mateusz.kulikowski@gmail.com>

Hi,

minor nits below.

[...]

> diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c
> new file mode 100644
> index 0000000..d17a29a
> --- /dev/null
> +++ b/drivers/usb/host/ehci-msm.c
> @@ -0,0 +1,198 @@
> +/*
> + * Qualcomm EHCI driver
> + *
> + * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
> + *
> + * Based on Linux driver
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + */
> +
> +#include <common.h>
> +#include <asm/gpio.h>
> +#include <asm-generic/errno.h>
> +#include <linux/compat.h>
> +#include <dm.h>
> +#include <fdtdec.h>
> +#include <libfdt.h>
> +#include <asm/io.h>
> +#include <usb.h>
> +#include <usb/ulpi.h>
> +#include "ehci.h"
> +
> +#ifndef CONFIG_USB_ULPI_VIEWPORT
> +#error Please enable CONFIG_USB_ULPI_VIEWPORT
> +#endif

The driver should select this in Kconfig instead of this check, right ?

> +#define MSM_USB_ULPI_OFFSET 0x170 /* ULPI viewport (PHY) */
> +#define MSM_USB_EHCI_OFFSET 0x100 /* Start of EHCI registers */
> +
> +/* PHY viewport regs */
> +#define ULPI_MISC_A_READ         0x96
> +#define ULPI_MISC_A_SET          0x97
> +#define ULPI_MISC_A_CLEAR        0x98
> +#define ULPI_MISC_A_VBUSVLDEXTSEL    (1 << 1)
> +#define ULPI_MISC_A_VBUSVLDEXT       (1 << 0)
> +
> +/* qcom specific registers (OTG) */
> +#define USB_GENCONFIG_2      0x00A0
> +#define GEN2_SESS_VLD_CTRL_EN (1 << 7)
> +
> +#define USB_USBCMD           (0x0140)

Please drop the parenthesis.

btw. this register layout looks very similar to struct usb_ehci in
include/usb/ehci-fsl.h , can the header be made more universal to
cover your driver as well ? Then these macros here won't be needed.

> +#define SESS_VLD_CTRL         (1 << 25)
> +#define USBCMD_RESET   2
> +#define USBCMD_ATTACH  1
> +
> +/* USB2_HSIC_USB_OTG_HS_BASE_USB_OTG_HS_PORTSC */
> +#define USB_PORTSC           0x0184
> +#define USB_SBUSCFG          0x0090
> +#define USB_AHB_MODE         0x0098
> +
> +#define USB_USBMODE          0x01A8
> +#define USBMODE_DEVICE 2
> +#define USBMODE_HOST   3
> +
> +struct msm_ehci_priv {
> +	struct ehci_ctrl ctrl; /* Needed by EHCI */
> +	phys_addr_t base;
> +	phys_addr_t ehci_base;
> +	u32 ulpi_base;
> +	u32 ulpi_port;
> +};
> +
> +int __weak board_prepare_usb(enum usb_init_type type)
> +{
> +	return 0;
> +}
> +
> +static void setup_usb_phy(struct msm_ehci_priv *priv)
> +{
> +	struct ulpi_viewport ulpi_vp = {.port_num = priv->ulpi_port,
> +					.viewport_addr = priv->ulpi_base};
> +
> +	/* Select and enable external configuration with USB PHY */
> +	ulpi_write(&ulpi_vp, (u8 *)ULPI_MISC_A_SET,
> +		   ULPI_MISC_A_VBUSVLDEXTSEL | ULPI_MISC_A_VBUSVLDEXT);
> +}
> +
> +static void reset_usb_phy(struct msm_ehci_priv *priv)
> +{
> +	struct ulpi_viewport ulpi_vp = {.port_num = priv->ulpi_port,
> +					.viewport_addr = priv->ulpi_base};
> +
> +	/* Disable VBUS mimicing in the controller. */
> +	ulpi_write(&ulpi_vp, (u8 *)ULPI_MISC_A_CLEAR,

This should be a pointer to a field in struct ulpi_regs, so the (u8 *)
cast does not seem right. See for example ehci-zynq.c

> +		   ULPI_MISC_A_VBUSVLDEXTSEL | ULPI_MISC_A_VBUSVLDEXT);
> +}


[...]

> +static int ehci_usb_remove(struct udevice *dev)
> +{
> +	struct msm_ehci_priv *p = dev_get_priv(dev);
> +	phys_addr_t reg = p->base + USB_USBCMD;
> +	int ret;
> +
> +	ret = ehci_deregister(dev);
> +	if (ret)
> +		return ret;
> +
> +	/* Stop controller. */
> +	writel(readl(reg) & ~USBCMD_ATTACH, reg);

This should use clrbits_le32() instead.

> +	reset_usb_phy(p);
> +
> +	ret = board_prepare_usb(USB_INIT_DEVICE); /* Board specific hook */
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Reset controller */
> +	writel(0x00080002, reg); /* reset usb */
> +	mdelay(20);
> +	/* Wait for completion */
> +	while (readl(reg) & 2)
> +		;

Look at wait_for_bit() implementations in the U-Boot tree and avoid the 
unbounded waiting here please.

> +	return 0;
> +}
> +
> +static int ehci_usb_ofdata_to_platdata(struct udevice *dev)
> +{
> +	struct msm_ehci_priv *priv = dev_get_priv(dev);
> +
> +	priv->base = dev_get_addr(dev);
> +	priv->ehci_base = priv->base + MSM_USB_EHCI_OFFSET;
> +	priv->ulpi_base = priv->base + MSM_USB_ULPI_OFFSET;
> +	priv->ulpi_port = 0;
> +	return 0;
> +}
> +
> +static const struct udevice_id ehci_usb_ids[] = {
> +	{ .compatible = "qcom,ehci-host", },
> +	{ }
> +};
> +
> +U_BOOT_DRIVER(usb_ehci) = {
> +	.name	= "ehci_msm",
> +	.id	= UCLASS_USB,
> +	.of_match = ehci_usb_ids,
> +	.ofdata_to_platdata = ehci_usb_ofdata_to_platdata,
> +	.probe = ehci_usb_probe,
> +	.remove = ehci_usb_remove,
> +	.ops	= &ehci_usb_ops,
> +	.priv_auto_alloc_size = sizeof(struct msm_ehci_priv),
> +	.flags	= DM_FLAG_ALLOC_PRIV_DMA,
> +};
Mateusz Kulikowski Dec. 13, 2015, 12:38 p.m. UTC | #2
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

Hi,

Thanks for quick review;

On 11.12.2015 00:22, Marek Vasut wrote:
> On Thursday, December 10, 2015 at 10:41:41 PM, Mateusz Kulikowski wrote:
[...]
>> +
>> +#ifndef CONFIG_USB_ULPI_VIEWPORT
>> +#error Please enable CONFIG_USB_ULPI_VIEWPORT
>> +#endif
> 
> The driver should select this in Kconfig instead of this check, right ?

That was my first attempt, but ULPI_VIEWPORT is not Kconfig option, 
and it seems it just doesn't work :(

It doesn't matter if I add it as 
select USB_ULPI_VIEWPORT
in usb KConfig, or forcibly add CONFIG_USB_ULPI_VIEWPORT to .config

[...]
>> +#define USB_USBCMD           (0x0140)
> 
> Please drop the parenthesis.

Doh, missed this one - surely will do it.

> 
> btw. this register layout looks very similar to struct usb_ehci in
> include/usb/ehci-fsl.h , can the header be made more universal to
> cover your driver as well ? Then these macros here won't be needed.

You're right.. in fact contrary to what I expected, Qualcomm didn't 
implemented their own USB controller.

It is designed by Chipidea, and PHY as far as I see is made by Synapsys.

I can use fsl headers with little exception that two registers are 
marked as reserved: USB_AHB_MODE (0x98) and USB_GENCONFIG_2 (0xA0)

My guess is that it's just different revision/config of IP core.

Do you think it wouldn't look awkward if I use fsl headers?

I think once this series gets to mainline we can create shared
driver that will support both vendors.

I also noticed that U-Boot have ci_udc already so there is 
a chance I make device controller working pretty fast 
(but I prefer not to include it in this series yet).

[...]
>> +	struct ulpi_viewport ulpi_vp = {.port_num = priv->ulpi_port,
>> +					.viewport_addr = priv->ulpi_base};
>> +
>> +	/* Disable VBUS mimicing in the controller. */
>> +	ulpi_write(&ulpi_vp, (u8 *)ULPI_MISC_A_CLEAR,
> 
> This should be a pointer to a field in struct ulpi_regs, so the (u8 *)
> cast does not seem right. See for example ehci-zynq.c
> 
Perhaps I misussed ulpi_viewport code in that case;

The reason is I need to access MISC_A register (0x96+) that is 
not in ulpi_regs structure - afaik it's vendor-specific.

Any hints how to tackle that properly?

I can of course duplicate ulpi code, but it probably doesn't make much sense.


[...]
>> +
>> +	/* Stop controller. */
>> +	writel(readl(reg) & ~USBCMD_ATTACH, reg);
> 
> This should use clrbits_le32() instead.

Ok

[...]
>> +	/* Wait for completion */
>> +	while (readl(reg) & 2)
>> +		;
> 
> Look at wait_for_bit() implementations in the U-Boot tree and avoid the 
> unbounded waiting here please.

Ok, btw I noticed there are 3 copies of almost the same code that does that :)

Perhaps I can add a patch to add this function to /lib as it seems it's 
common use case?

The following drivers would benefit: ehci-msm, zynq_gem, dwc2, 
ehci-mx6, ohci-lpc32xx


Regards,
Mateusz
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2

iQEcBAEBCAAGBQJWbWbJAAoJELvtohmVtQzBvEEH/iwqONAqWwqQzpUR4izzZ97Y
CAEUWi4GacxwUVt0vZMcK5KV0sRJVP947daMxVkNoDWWkpREuPby+OecFe3mk7iJ
cJzTAlYs/OOIkGBuza2wkfaxXq0AItpn2lBF/Vwe8u5hFGSPgYY0quek8SKma6NQ
rtNFVdc+4+pgGMy1Pl8Fym9UXOJ/aVv806+XS34UrgGSsnv5qWudRiT3HA0ZR38A
VPzgRXs+kIwVAhPe2AlXW0K8w/ipaEF41qAMvHUdXopi0h4Tgsc2QEijC0sIQmBf
kSM6gvzYq+gFOJifxcEt3EJj6hOQ4U7nEOi/PqtjBl3BsTw6IdUWLdakCVzQq1I=
=eUF2
-----END PGP SIGNATURE-----
Marek Vasut Dec. 13, 2015, 3:48 p.m. UTC | #3
On Sunday, December 13, 2015 at 01:38:41 PM, Mateusz Kulikowski wrote:
> Hi,
> 
> Thanks for quick review;
> 
> On 11.12.2015 00:22, Marek Vasut wrote:
> > On Thursday, December 10, 2015 at 10:41:41 PM, Mateusz Kulikowski wrote:
> [...]
> 
> >> +
> >> +#ifndef CONFIG_USB_ULPI_VIEWPORT
> >> +#error Please enable CONFIG_USB_ULPI_VIEWPORT
> >> +#endif
> > 
> > The driver should select this in Kconfig instead of this check, right ?
> 
> That was my first attempt, but ULPI_VIEWPORT is not Kconfig option,
> and it seems it just doesn't work :(
> 
> It doesn't matter if I add it as
> select USB_ULPI_VIEWPORT
> in usb KConfig, or forcibly add CONFIG_USB_ULPI_VIEWPORT to .config

I think it should be quite easily possible to add this to USB Kconfig.

> [...]
> 
> >> +#define USB_USBCMD           (0x0140)
> > 
> > Please drop the parenthesis.
> 
> Doh, missed this one - surely will do it.
> 
> > btw. this register layout looks very similar to struct usb_ehci in
> > include/usb/ehci-fsl.h , can the header be made more universal to
> > cover your driver as well ? Then these macros here won't be needed.
> 
> You're right.. in fact contrary to what I expected, Qualcomm didn't
> implemented their own USB controller.

Well building a chip is like going to a IP block supermarket afterall ;-)

> It is designed by Chipidea, and PHY as far as I see is made by Synapsys.

All of the drivers/usb/host/ehci-{mxs,mx5,mx6,vf}.c are also chipidea
cores. MXS and MX6 have chipidea PHY too.

> I can use fsl headers with little exception that two registers are
> marked as reserved: USB_AHB_MODE (0x98) and USB_GENCONFIG_2 (0xA0)
> 
> My guess is that it's just different revision/config of IP core.
> 
> Do you think it wouldn't look awkward if I use fsl headers?

Just rename them to ehci-ci.h, that should be the quickest.

> I think once this series gets to mainline we can create shared
> driver that will support both vendors.

I'm all for that.

> I also noticed that U-Boot have ci_udc already so there is
> a chance I make device controller working pretty fast
> (but I prefer not to include it in this series yet).

Correct, that works too. I think it was also tested on some marvell chip.

> [...]
> 
> >> +	struct ulpi_viewport ulpi_vp = {.port_num = priv->ulpi_port,
> >> +					.viewport_addr = priv->ulpi_base};
> >> +
> >> +	/* Disable VBUS mimicing in the controller. */
> >> +	ulpi_write(&ulpi_vp, (u8 *)ULPI_MISC_A_CLEAR,
> > 
> > This should be a pointer to a field in struct ulpi_regs, so the (u8 *)
> > cast does not seem right. See for example ehci-zynq.c
> 
> Perhaps I misussed ulpi_viewport code in that case;
> 
> The reason is I need to access MISC_A register (0x96+) that is
> not in ulpi_regs structure - afaik it's vendor-specific.
> 
> Any hints how to tackle that properly?
> 
> I can of course duplicate ulpi code, but it probably doesn't make much
> sense.

I don't have a better suggestion, sorry. Let's keep this as-is unless
someone can come up with something better. Code duplication is not a
good idea, so we won't do that.

> [...]
> 
> >> +
> >> +	/* Stop controller. */
> >> +	writel(readl(reg) & ~USBCMD_ATTACH, reg);
> > 
> > This should use clrbits_le32() instead.
> 
> Ok
> 
> [...]
> 
> >> +	/* Wait for completion */
> >> +	while (readl(reg) & 2)
> >> +		;
> > 
> > Look at wait_for_bit() implementations in the U-Boot tree and avoid the
> > unbounded waiting here please.
> 
> Ok, btw I noticed there are 3 copies of almost the same code that does that
> :)
> 
> Perhaps I can add a patch to add this function to /lib as it seems it's
> common use case?

That'd be great, it was on my list to do it for a while, but I didn't get around 
doing that yet.

> The following drivers would benefit: ehci-msm, zynq_gem, dwc2,
> ehci-mx6, ohci-lpc32xx

Thanks!
Mateusz Kulikowski Dec. 16, 2015, 10:51 p.m. UTC | #4
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

Hi Marek,

On 13.12.2015 16:48, Marek Vasut wrote:
> On Sunday, December 13, 2015 at 01:38:41 PM, Mateusz Kulikowski wrote:
[...]
>> It doesn't matter if I add it as
>> select USB_ULPI_VIEWPORT
>> in usb KConfig, or forcibly add CONFIG_USB_ULPI_VIEWPORT to .config
> 
> I think it should be quite easily possible to add this to USB Kconfig.

Will do.

>> I can use fsl headers with little exception that two registers are
>> marked as reserved: USB_AHB_MODE (0x98) and USB_GENCONFIG_2 (0xA0)
>>
>> My guess is that it's just different revision/config of IP core.
>>
>> Do you think it wouldn't look awkward if I use fsl headers?
> 
> Just rename them to ehci-ci.h, that should be the quickest.

Will do.

[...]
>>>> +	struct ulpi_viewport ulpi_vp = {.port_num = priv->ulpi_port,
>>>> +					.viewport_addr = priv->ulpi_base};
>>>> +
>>>> +	/* Disable VBUS mimicing in the controller. */
>>>> +	ulpi_write(&ulpi_vp, (u8 *)ULPI_MISC_A_CLEAR,
>>>
>>> This should be a pointer to a field in struct ulpi_regs, so the (u8 *)
>>> cast does not seem right. See for example ehci-zynq.c
>>
>> Perhaps I misussed ulpi_viewport code in that case;
>>
>> The reason is I need to access MISC_A register (0x96+) that is
>> not in ulpi_regs structure - afaik it's vendor-specific.
>>
>> Any hints how to tackle that properly?
>>
>> I can of course duplicate ulpi code, but it probably doesn't make much
>> sense.
> 
> I don't have a better suggestion, sorry. Let's keep this as-is unless
> someone can come up with something better. Code duplication is not a
> good idea, so we won't do that.

OK

Thanks, 
Mateusz
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2

iQEcBAEBCAAGBQJWcerfAAoJELvtohmVtQzB1AMH/260Ioo6W6+skWJJhmm+g3yD
B3kWhJWWyGPSkY4QcA7O/dgzU9i/n9a6gwlqcwYLhYlzyKG/p0aRBim0RWEFWK+S
V1SMYA3iQhYLkjMdOfqNXdQ5NrG2osll1Nk2GK9wBthVtPkjqfQkOupw7oZq3kd5
3pNpbsREIuxfct26C7kaTJccRf7MIYdlslYu53h4T/t03oO8xBIrSDGAR+9UD841
yCWuATbg461uhPrD1/WBG/wiIzMaOsITyiXIkxr7Z0GNrOeaZ38YqSwASwLTUmxO
NB+UkOvA9eM1oz79Qaoxyrfm/rDtds+CHXuFco2LY2gOl1280ffwKG1UQWCWDsk=
=XMV7
-----END PGP SIGNATURE-----
diff mbox

Patch

diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 0096a2f..5a63475 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -66,6 +66,14 @@  config USB_EHCI_MX6
 	---help---
 	  Enables support for the on-chip EHCI controller on i.MX6 SoCs.
 
+config USB_EHCI_MSM
+	bool "Support for Qualcomm on-chip EHCI USB controller"
+	depends on DM_USB
+	default n
+	---help---
+	  Enables support for the on-chip EHCI controller on Qualcomm
+	  Snapdragon SoCs.
+
 config USB_EHCI_UNIPHIER
 	bool "Support for UniPhier on-chip EHCI USB controller"
 	depends on ARCH_UNIPHIER && OF_CONTROL
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index 0b4b458..d4a556a 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -42,6 +42,7 @@  obj-$(CONFIG_USB_EHCI_MX7) += ehci-mx6.o
 obj-$(CONFIG_USB_EHCI_OMAP) += ehci-omap.o
 obj-$(CONFIG_USB_EHCI_PPC4XX) += ehci-ppc4xx.o
 obj-$(CONFIG_USB_EHCI_MARVELL) += ehci-marvell.o
+obj-$(CONFIG_USB_EHCI_MSM) += ehci-msm.o
 obj-$(CONFIG_USB_EHCI_PCI) += ehci-pci.o
 obj-$(CONFIG_USB_EHCI_SPEAR) += ehci-spear.o
 obj-$(CONFIG_USB_EHCI_SUNXI) += ehci-sunxi.o
diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c
new file mode 100644
index 0000000..d17a29a
--- /dev/null
+++ b/drivers/usb/host/ehci-msm.c
@@ -0,0 +1,198 @@ 
+/*
+ * Qualcomm EHCI driver
+ *
+ * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
+ *
+ * Based on Linux driver
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/gpio.h>
+#include <asm-generic/errno.h>
+#include <linux/compat.h>
+#include <dm.h>
+#include <fdtdec.h>
+#include <libfdt.h>
+#include <asm/io.h>
+#include <usb.h>
+#include <usb/ulpi.h>
+#include "ehci.h"
+
+#ifndef CONFIG_USB_ULPI_VIEWPORT
+#error Please enable CONFIG_USB_ULPI_VIEWPORT
+#endif
+
+#define MSM_USB_ULPI_OFFSET 0x170 /* ULPI viewport (PHY) */
+#define MSM_USB_EHCI_OFFSET 0x100 /* Start of EHCI registers */
+
+/* PHY viewport regs */
+#define ULPI_MISC_A_READ         0x96
+#define ULPI_MISC_A_SET          0x97
+#define ULPI_MISC_A_CLEAR        0x98
+#define ULPI_MISC_A_VBUSVLDEXTSEL    (1 << 1)
+#define ULPI_MISC_A_VBUSVLDEXT       (1 << 0)
+
+/* qcom specific registers (OTG) */
+#define USB_GENCONFIG_2      0x00A0
+#define GEN2_SESS_VLD_CTRL_EN (1 << 7)
+
+#define USB_USBCMD           (0x0140)
+#define SESS_VLD_CTRL         (1 << 25)
+#define USBCMD_RESET   2
+#define USBCMD_ATTACH  1
+
+/* USB2_HSIC_USB_OTG_HS_BASE_USB_OTG_HS_PORTSC */
+#define USB_PORTSC           0x0184
+#define USB_SBUSCFG          0x0090
+#define USB_AHB_MODE         0x0098
+
+#define USB_USBMODE          0x01A8
+#define USBMODE_DEVICE 2
+#define USBMODE_HOST   3
+
+struct msm_ehci_priv {
+	struct ehci_ctrl ctrl; /* Needed by EHCI */
+	phys_addr_t base;
+	phys_addr_t ehci_base;
+	u32 ulpi_base;
+	u32 ulpi_port;
+};
+
+int __weak board_prepare_usb(enum usb_init_type type)
+{
+	return 0;
+}
+
+static void setup_usb_phy(struct msm_ehci_priv *priv)
+{
+	struct ulpi_viewport ulpi_vp = {.port_num = priv->ulpi_port,
+					.viewport_addr = priv->ulpi_base};
+
+	/* Select and enable external configuration with USB PHY */
+	ulpi_write(&ulpi_vp, (u8 *)ULPI_MISC_A_SET,
+		   ULPI_MISC_A_VBUSVLDEXTSEL | ULPI_MISC_A_VBUSVLDEXT);
+}
+
+static void reset_usb_phy(struct msm_ehci_priv *priv)
+{
+	struct ulpi_viewport ulpi_vp = {.port_num = priv->ulpi_port,
+					.viewport_addr = priv->ulpi_base};
+
+	/* Disable VBUS mimicing in the controller. */
+	ulpi_write(&ulpi_vp, (u8 *)ULPI_MISC_A_CLEAR,
+		   ULPI_MISC_A_VBUSVLDEXTSEL | ULPI_MISC_A_VBUSVLDEXT);
+}
+
+
+static int msm_init_after_reset(struct ehci_ctrl *dev)
+{
+	struct msm_ehci_priv *p = container_of(dev, struct msm_ehci_priv, ctrl);
+	uint32_t val;
+
+	/* select ULPI phy */
+	writel(0x80000000, p->base + USB_PORTSC);
+	setup_usb_phy(p);
+
+	/* Enable sess_vld */
+	val = readl(p->base + USB_GENCONFIG_2) | GEN2_SESS_VLD_CTRL_EN;
+	writel(val, p->base + USB_GENCONFIG_2);
+
+	/* Enable external vbus configuration in the LINK */
+	val = readl(p->base + USB_USBCMD);
+	val |= SESS_VLD_CTRL;
+	writel(val, p->base + USB_USBCMD);
+
+	/* USB_OTG_HS_AHB_BURST */
+	writel(0x0, p->base + USB_SBUSCFG);
+
+	/* USB_OTG_HS_AHB_MODE: HPROT_MODE */
+	/* Bus access related config. */
+	writel(0x08, p->base + USB_AHB_MODE);
+
+	/* set mode to host controller */
+	writel(USBMODE_HOST, p->base + USB_USBMODE);
+
+	return 0;
+}
+
+static const struct ehci_ops msm_ehci_ops = {
+	.init_after_reset = msm_init_after_reset
+};
+
+static int ehci_usb_probe(struct udevice *dev)
+{
+	struct msm_ehci_priv *p = dev_get_priv(dev);
+	struct ehci_hccr *cr;
+	struct ehci_hcor *or;
+	int ret;
+
+	cr = (struct ehci_hccr *)p->ehci_base;
+	or = (struct ehci_hcor *)(p->ehci_base +
+				  HC_LENGTH(readl(p->ehci_base)));
+
+	ret = board_prepare_usb(USB_INIT_HOST);
+	if (ret < 0)
+		return ret;
+
+
+	return ehci_register(dev, cr, or, &msm_ehci_ops, 0, USB_INIT_HOST);
+}
+
+static int ehci_usb_remove(struct udevice *dev)
+{
+	struct msm_ehci_priv *p = dev_get_priv(dev);
+	phys_addr_t reg = p->base + USB_USBCMD;
+	int ret;
+
+	ret = ehci_deregister(dev);
+	if (ret)
+		return ret;
+
+	/* Stop controller. */
+	writel(readl(reg) & ~USBCMD_ATTACH, reg);
+
+	reset_usb_phy(p);
+
+	ret = board_prepare_usb(USB_INIT_DEVICE); /* Board specific hook */
+	if (ret < 0)
+		return ret;
+
+	/* Reset controller */
+	writel(0x00080002, reg); /* reset usb */
+	mdelay(20);
+	/* Wait for completion */
+	while (readl(reg) & 2)
+		;
+
+	return 0;
+}
+
+static int ehci_usb_ofdata_to_platdata(struct udevice *dev)
+{
+	struct msm_ehci_priv *priv = dev_get_priv(dev);
+
+	priv->base = dev_get_addr(dev);
+	priv->ehci_base = priv->base + MSM_USB_EHCI_OFFSET;
+	priv->ulpi_base = priv->base + MSM_USB_ULPI_OFFSET;
+	priv->ulpi_port = 0;
+	return 0;
+}
+
+static const struct udevice_id ehci_usb_ids[] = {
+	{ .compatible = "qcom,ehci-host", },
+	{ }
+};
+
+U_BOOT_DRIVER(usb_ehci) = {
+	.name	= "ehci_msm",
+	.id	= UCLASS_USB,
+	.of_match = ehci_usb_ids,
+	.ofdata_to_platdata = ehci_usb_ofdata_to_platdata,
+	.probe = ehci_usb_probe,
+	.remove = ehci_usb_remove,
+	.ops	= &ehci_usb_ops,
+	.priv_auto_alloc_size = sizeof(struct msm_ehci_priv),
+	.flags	= DM_FLAG_ALLOC_PRIV_DMA,
+};