summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
authorStephen Warren <swarren@nvidia.com>2012-09-14 11:35:16 -0600
committerStephen Warren <swarren@nvidia.com>2012-09-14 11:35:16 -0600
commitfef40b2369a4ececa16b4d02b625e45252fe5d0a (patch)
tree22445e45f0882630e410b89e2a26fd3e9ba9c30b /drivers/usb
parentf49540d1d7e90dc9864d96ad5a505d7feb8efbcc (diff)
parent363366cf61c544ea476f3d220f43a95cb03014f5 (diff)
downloadlinux-fef40b2369a4ececa16b4d02b625e45252fe5d0a.tar.gz
Merge commit 'xceiv-for-v3.7' into for-3.7/cleanup2
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/dwc3/core.c15
-rw-r--r--drivers/usb/dwc3/core.h5
-rw-r--r--drivers/usb/dwc3/dwc3-exynos.c66
-rw-r--r--drivers/usb/dwc3/dwc3-omap.c66
-rw-r--r--drivers/usb/dwc3/dwc3-pci.c67
-rw-r--r--drivers/usb/host/ehci-tegra.c14
-rw-r--r--drivers/usb/musb/am35x.c1
-rw-r--r--drivers/usb/musb/blackfin.c1
-rw-r--r--drivers/usb/musb/da8xx.c1
-rw-r--r--drivers/usb/musb/davinci.c1
-rw-r--r--drivers/usb/musb/musb_dsps.c1
-rw-r--r--drivers/usb/musb/tusb6010.c1
-rw-r--r--drivers/usb/otg/Kconfig2
-rw-r--r--drivers/usb/otg/fsl_otg.c34
-rw-r--r--drivers/usb/otg/mxs-phy.c38
-rw-r--r--drivers/usb/otg/nop-usb-xceiv.c8
-rw-r--r--drivers/usb/otg/otg.c2
-rw-r--r--drivers/usb/otg/twl4030-usb.c26
-rw-r--r--drivers/usb/otg/twl6030-usb.c157
-rw-r--r--drivers/usb/phy/Kconfig17
-rw-r--r--drivers/usb/phy/Makefile3
-rw-r--r--drivers/usb/phy/isp1301.c6
-rw-r--r--drivers/usb/phy/mv_u3d_phy.c345
-rw-r--r--drivers/usb/phy/mv_u3d_phy.h105
-rw-r--r--drivers/usb/phy/omap-usb2.c271
-rw-r--r--drivers/usb/phy/tegra_usb_phy.c839
26 files changed, 1945 insertions, 147 deletions
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index c34452a7304f..79a24fab13d1 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -50,6 +50,7 @@
#include <linux/dma-mapping.h>
#include <linux/of.h>
+#include <linux/usb/otg.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
@@ -136,6 +137,8 @@ static void dwc3_core_soft_reset(struct dwc3 *dwc)
reg |= DWC3_GUSB2PHYCFG_PHYSOFTRST;
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ usb_phy_init(dwc->usb2_phy);
+ usb_phy_init(dwc->usb3_phy);
mdelay(100);
/* Clear USB3 PHY reset */
@@ -465,6 +468,18 @@ static int __devinit dwc3_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ dwc->usb2_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
+ if (IS_ERR_OR_NULL(dwc->usb2_phy)) {
+ dev_err(dev, "no usb2 phy configured\n");
+ return -EPROBE_DEFER;
+ }
+
+ dwc->usb3_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3);
+ if (IS_ERR_OR_NULL(dwc->usb3_phy)) {
+ dev_err(dev, "no usb3 phy configured\n");
+ return -EPROBE_DEFER;
+ }
+
spin_lock_init(&dwc->lock);
platform_set_drvdata(pdev, dwc);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 151eca876dfd..dbc5713d84fb 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -624,6 +624,8 @@ struct dwc3_scratchpad_array {
* @maximum_speed: maximum speed requested (mainly for testing purposes)
* @revision: revision register contents
* @mode: mode of operation
+ * @usb2_phy: pointer to USB2 PHY
+ * @usb3_phy: pointer to USB3 PHY
* @is_selfpowered: true when we are selfpowered
* @three_stage_setup: set if we perform a three phase setup
* @ep0_bounced: true when we used bounce buffer
@@ -667,6 +669,9 @@ struct dwc3 {
struct usb_gadget gadget;
struct usb_gadget_driver *gadget_driver;
+ struct usb_phy *usb2_phy;
+ struct usb_phy *usb3_phy;
+
void __iomem *regs;
size_t regs_size;
diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c
index b8f00389fa34..ca6597853f90 100644
--- a/drivers/usb/dwc3/dwc3-exynos.c
+++ b/drivers/usb/dwc3/dwc3-exynos.c
@@ -19,16 +19,74 @@
#include <linux/platform_data/dwc3-exynos.h>
#include <linux/dma-mapping.h>
#include <linux/clk.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/nop-usb-xceiv.h>
#include "core.h"
struct dwc3_exynos {
struct platform_device *dwc3;
+ struct platform_device *usb2_phy;
+ struct platform_device *usb3_phy;
struct device *dev;
struct clk *clk;
};
+static int __devinit dwc3_exynos_register_phys(struct dwc3_exynos *exynos)
+{
+ struct nop_usb_xceiv_platform_data pdata;
+ struct platform_device *pdev;
+ int ret;
+
+ memset(&pdata, 0x00, sizeof(pdata));
+
+ pdev = platform_device_alloc("nop_usb_xceiv", 0);
+ if (!pdev)
+ return -ENOMEM;
+
+ exynos->usb2_phy = pdev;
+ pdata.type = USB_PHY_TYPE_USB2;
+
+ ret = platform_device_add_data(exynos->usb2_phy, &pdata, sizeof(pdata));
+ if (ret)
+ goto err1;
+
+ pdev = platform_device_alloc("nop_usb_xceiv", 1);
+ if (!pdev) {
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ exynos->usb3_phy = pdev;
+ pdata.type = USB_PHY_TYPE_USB3;
+
+ ret = platform_device_add_data(exynos->usb3_phy, &pdata, sizeof(pdata));
+ if (ret)
+ goto err2;
+
+ ret = platform_device_add(exynos->usb2_phy);
+ if (ret)
+ goto err2;
+
+ ret = platform_device_add(exynos->usb3_phy);
+ if (ret)
+ goto err3;
+
+ return 0;
+
+err3:
+ platform_device_del(exynos->usb2_phy);
+
+err2:
+ platform_device_put(exynos->usb3_phy);
+
+err1:
+ platform_device_put(exynos->usb2_phy);
+
+ return ret;
+}
+
static int __devinit dwc3_exynos_probe(struct platform_device *pdev)
{
struct dwc3_exynos_data *pdata = pdev->dev.platform_data;
@@ -51,6 +109,12 @@ static int __devinit dwc3_exynos_probe(struct platform_device *pdev)
if (devid < 0)
goto err1;
+ ret = dwc3_exynos_register_phys(exynos);
+ if (ret) {
+ dev_err(&pdev->dev, "couldn't register PHYs\n");
+ goto err1;
+ }
+
dwc3 = platform_device_alloc("dwc3", devid);
if (!dwc3) {
dev_err(&pdev->dev, "couldn't allocate dwc3 device\n");
@@ -120,6 +184,8 @@ static int __devexit dwc3_exynos_remove(struct platform_device *pdev)
struct dwc3_exynos_data *pdata = pdev->dev.platform_data;
platform_device_unregister(exynos->dwc3);
+ platform_device_unregister(exynos->usb2_phy);
+ platform_device_unregister(exynos->usb3_phy);
dwc3_put_device_id(exynos->dwc3->id);
diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c
index 479dc047da3a..ee57a10d90d0 100644
--- a/drivers/usb/dwc3/dwc3-omap.c
+++ b/drivers/usb/dwc3/dwc3-omap.c
@@ -48,6 +48,9 @@
#include <linux/io.h>
#include <linux/of.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/nop-usb-xceiv.h>
+
#include "core.h"
/*
@@ -131,6 +134,8 @@ struct dwc3_omap {
spinlock_t lock;
struct platform_device *dwc3;
+ struct platform_device *usb2_phy;
+ struct platform_device *usb3_phy;
struct device *dev;
int irq;
@@ -152,6 +157,59 @@ static inline void dwc3_omap_writel(void __iomem *base, u32 offset, u32 value)
writel(value, base + offset);
}
+static int __devinit dwc3_omap_register_phys(struct dwc3_omap *omap)
+{
+ struct nop_usb_xceiv_platform_data pdata;
+ struct platform_device *pdev;
+ int ret;
+
+ memset(&pdata, 0x00, sizeof(pdata));
+
+ pdev = platform_device_alloc("nop_usb_xceiv", 0);
+ if (!pdev)
+ return -ENOMEM;
+
+ omap->usb2_phy = pdev;
+ pdata.type = USB_PHY_TYPE_USB2;
+
+ ret = platform_device_add_data(omap->usb2_phy, &pdata, sizeof(pdata));
+ if (ret)
+ goto err1;
+
+ pdev = platform_device_alloc("nop_usb_xceiv", 1);
+ if (!pdev) {
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ omap->usb3_phy = pdev;
+ pdata.type = USB_PHY_TYPE_USB3;
+
+ ret = platform_device_add_data(omap->usb3_phy, &pdata, sizeof(pdata));
+ if (ret)
+ goto err2;
+
+ ret = platform_device_add(omap->usb2_phy);
+ if (ret)
+ goto err2;
+
+ ret = platform_device_add(omap->usb3_phy);
+ if (ret)
+ goto err3;
+
+ return 0;
+
+err3:
+ platform_device_del(omap->usb2_phy);
+
+err2:
+ platform_device_put(omap->usb3_phy);
+
+err1:
+ platform_device_put(omap->usb2_phy);
+
+ return ret;
+}
static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
{
@@ -251,6 +309,12 @@ static int __devinit dwc3_omap_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ ret = dwc3_omap_register_phys(omap);
+ if (ret) {
+ dev_err(dev, "couldn't register PHYs\n");
+ return ret;
+ }
+
devid = dwc3_get_device_id();
if (devid < 0)
return -ENODEV;
@@ -371,6 +435,8 @@ static int __devexit dwc3_omap_remove(struct platform_device *pdev)
struct dwc3_omap *omap = platform_get_drvdata(pdev);
platform_device_unregister(omap->dwc3);
+ platform_device_unregister(omap->usb2_phy);
+ platform_device_unregister(omap->usb3_phy);
dwc3_put_device_id(omap->dwc3->id);
diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c
index a9ca9adba391..94f550e37f98 100644
--- a/drivers/usb/dwc3/dwc3-pci.c
+++ b/drivers/usb/dwc3/dwc3-pci.c
@@ -42,6 +42,9 @@
#include <linux/pci.h>
#include <linux/platform_device.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/nop-usb-xceiv.h>
+
#include "core.h"
/* FIXME define these in <linux/pci_ids.h> */
@@ -51,8 +54,64 @@
struct dwc3_pci {
struct device *dev;
struct platform_device *dwc3;
+ struct platform_device *usb2_phy;
+ struct platform_device *usb3_phy;
};
+static int __devinit dwc3_pci_register_phys(struct dwc3_pci *glue)
+{
+ struct nop_usb_xceiv_platform_data pdata;
+ struct platform_device *pdev;
+ int ret;
+
+ memset(&pdata, 0x00, sizeof(pdata));
+
+ pdev = platform_device_alloc("nop_usb_xceiv", 0);
+ if (!pdev)
+ return -ENOMEM;
+
+ glue->usb2_phy = pdev;
+ pdata.type = USB_PHY_TYPE_USB2;
+
+ ret = platform_device_add_data(glue->usb2_phy, &pdata, sizeof(pdata));
+ if (ret)
+ goto err1;
+
+ pdev = platform_device_alloc("nop_usb_xceiv", 1);
+ if (!pdev) {
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ glue->usb3_phy = pdev;
+ pdata.type = USB_PHY_TYPE_USB3;
+
+ ret = platform_device_add_data(glue->usb3_phy, &pdata, sizeof(pdata));
+ if (ret)
+ goto err2;
+
+ ret = platform_device_add(glue->usb2_phy);
+ if (ret)
+ goto err2;
+
+ ret = platform_device_add(glue->usb3_phy);
+ if (ret)
+ goto err3;
+
+ return 0;
+
+err3:
+ platform_device_del(glue->usb2_phy);
+
+err2:
+ platform_device_put(glue->usb3_phy);
+
+err1:
+ platform_device_put(glue->usb2_phy);
+
+ return ret;
+}
+
static int __devinit dwc3_pci_probe(struct pci_dev *pci,
const struct pci_device_id *id)
{
@@ -80,6 +139,12 @@ static int __devinit dwc3_pci_probe(struct pci_dev *pci,
pci_set_power_state(pci, PCI_D0);
pci_set_master(pci);
+ ret = dwc3_pci_register_phys(glue);
+ if (ret) {
+ dev_err(dev, "couldn't register PHYs\n");
+ return ret;
+ }
+
devid = dwc3_get_device_id();
if (devid < 0) {
ret = -ENOMEM;
@@ -144,6 +209,8 @@ static void __devexit dwc3_pci_remove(struct pci_dev *pci)
{
struct dwc3_pci *glue = pci_get_drvdata(pci);
+ platform_device_unregister(glue->usb2_phy);
+ platform_device_unregister(glue->usb3_phy);
dwc3_put_device_id(glue->dwc3->id);
platform_device_unregister(glue->dwc3);
pci_set_drvdata(pci, NULL);
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c
index 26dedb30ad0b..c0d47323b52a 100644
--- a/drivers/usb/host/ehci-tegra.c
+++ b/drivers/usb/host/ehci-tegra.c
@@ -27,7 +27,7 @@
#include <linux/of_gpio.h>
#include <linux/pm_runtime.h>
-#include <mach/usb_phy.h>
+#include <linux/usb/tegra_usb_phy.h>
#include <mach/iomap.h>
#define TEGRA_USB_DMA_ALIGN 32
@@ -49,7 +49,7 @@ static void tegra_ehci_power_up(struct usb_hcd *hcd)
clk_prepare_enable(tegra->emc_clk);
clk_prepare_enable(tegra->clk);
- tegra_usb_phy_power_on(tegra->phy);
+ usb_phy_set_suspend(&tegra->phy->u_phy, 0);
tegra->host_resumed = 1;
}
@@ -58,7 +58,7 @@ static void tegra_ehci_power_down(struct usb_hcd *hcd)
struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller);
tegra->host_resumed = 0;
- tegra_usb_phy_power_off(tegra->phy);
+ usb_phy_set_suspend(&tegra->phy->u_phy, 1);
clk_disable_unprepare(tegra->clk);
clk_disable_unprepare(tegra->emc_clk);
}
@@ -715,7 +715,9 @@ static int tegra_ehci_probe(struct platform_device *pdev)
goto fail_phy;
}
- err = tegra_usb_phy_power_on(tegra->phy);
+ usb_phy_init(&tegra->phy->u_phy);
+
+ err = usb_phy_set_suspend(&tegra->phy->u_phy, 0);
if (err) {
dev_err(&pdev->dev, "Failed to power on the phy\n");
goto fail;
@@ -762,7 +764,7 @@ fail:
usb_put_phy(tegra->transceiver);
}
#endif
- tegra_usb_phy_close(tegra->phy);
+ usb_phy_shutdown(&tegra->phy->u_phy);
fail_phy:
iounmap(hcd->regs);
fail_io:
@@ -800,7 +802,7 @@ static int tegra_ehci_remove(struct platform_device *pdev)
usb_remove_hcd(hcd);
- tegra_usb_phy_close(tegra->phy);
+ usb_phy_shutdown(&tegra->phy->u_phy);
iounmap(hcd->regs);
usb_put_hcd(hcd);
diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c
index 7a95ab87ac00..5d64c5b5ef52 100644
--- a/drivers/usb/musb/am35x.c
+++ b/drivers/usb/musb/am35x.c
@@ -33,6 +33,7 @@
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
+#include <linux/usb/nop-usb-xceiv.h>
#include <plat/usb.h>
diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c
index 428e6aa3e78a..b562623a8971 100644
--- a/drivers/usb/musb/blackfin.c
+++ b/drivers/usb/musb/blackfin.c
@@ -19,6 +19,7 @@
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/prefetch.h>
+#include <linux/usb/nop-usb-xceiv.h>
#include <asm/cacheflush.h>
diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c
index 0f9fcec4e1d3..16798c9e6f38 100644
--- a/drivers/usb/musb/da8xx.c
+++ b/drivers/usb/musb/da8xx.c
@@ -33,6 +33,7 @@
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
+#include <linux/usb/nop-usb-xceiv.h>
#include <mach/da8xx.h>
#include <mach/usb.h>
diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c
index 472c8b42d38b..863a9b6286c3 100644
--- a/drivers/usb/musb/davinci.c
+++ b/drivers/usb/musb/davinci.c
@@ -33,6 +33,7 @@
#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
+#include <linux/usb/nop-usb-xceiv.h>
#include <mach/cputype.h>
#include <mach/hardware.h>
diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c
index 494772fc9e23..c20b8776aafa 100644
--- a/drivers/usb/musb/musb_dsps.c
+++ b/drivers/usb/musb/musb_dsps.c
@@ -36,6 +36,7 @@
#include <linux/dma-mapping.h>
#include <linux/pm_runtime.h>
#include <linux/module.h>
+#include <linux/usb/nop-usb-xceiv.h>
#include <linux/of.h>
#include <linux/of_device.h>
diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c
index 1a1bd9cf40c5..00f21dfee5d7 100644
--- a/drivers/usb/musb/tusb6010.c
+++ b/drivers/usb/musb/tusb6010.c
@@ -24,6 +24,7 @@
#include <linux/irq.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
+#include <linux/usb/nop-usb-xceiv.h>
#include "musb_core.h"
diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig
index 13fd1ddf742f..d8c8a42bff3e 100644
--- a/drivers/usb/otg/Kconfig
+++ b/drivers/usb/otg/Kconfig
@@ -68,7 +68,7 @@ config TWL4030_USB
config TWL6030_USB
tristate "TWL6030 USB Transceiver Driver"
- depends on TWL4030_CORE
+ depends on TWL4030_CORE && OMAP_USB2
select USB_OTG_UTILS
help
Enable this to support the USB OTG transceiver on TWL6030
diff --git a/drivers/usb/otg/fsl_otg.c b/drivers/usb/otg/fsl_otg.c
index 23c798cb2d7f..c19d1d7173a9 100644
--- a/drivers/usb/otg/fsl_otg.c
+++ b/drivers/usb/otg/fsl_otg.c
@@ -544,9 +544,13 @@ int fsl_otg_start_gadget(struct otg_fsm *fsm, int on)
*/
static int fsl_otg_set_host(struct usb_otg *otg, struct usb_bus *host)
{
- struct fsl_otg *otg_dev = container_of(otg->phy, struct fsl_otg, phy);
+ struct fsl_otg *otg_dev;
+
+ if (!otg)
+ return -ENODEV;
- if (!otg || otg_dev != fsl_otg_dev)
+ otg_dev = container_of(otg->phy, struct fsl_otg, phy);
+ if (otg_dev != fsl_otg_dev)
return -ENODEV;
otg->host = host;
@@ -590,12 +594,15 @@ static int fsl_otg_set_host(struct usb_otg *otg, struct usb_bus *host)
static int fsl_otg_set_peripheral(struct usb_otg *otg,
struct usb_gadget *gadget)
{
- struct fsl_otg *otg_dev = container_of(otg->phy, struct fsl_otg, phy);
+ struct fsl_otg *otg_dev;
+ if (!otg)
+ return -ENODEV;
+
+ otg_dev = container_of(otg->phy, struct fsl_otg, phy);
VDBG("otg_dev 0x%x\n", (int)otg_dev);
VDBG("fsl_otg_dev 0x%x\n", (int)fsl_otg_dev);
-
- if (!otg || otg_dev != fsl_otg_dev)
+ if (otg_dev != fsl_otg_dev)
return -ENODEV;
if (!gadget) {
@@ -660,10 +667,13 @@ static void fsl_otg_event(struct work_struct *work)
/* B-device start SRP */
static int fsl_otg_start_srp(struct usb_otg *otg)
{
- struct fsl_otg *otg_dev = container_of(otg->phy, struct fsl_otg, phy);
+ struct fsl_otg *otg_dev;
+
+ if (!otg || otg->phy->state != OTG_STATE_B_IDLE)
+ return -ENODEV;
- if (!otg || otg_dev != fsl_otg_dev
- || otg->phy->state != OTG_STATE_B_IDLE)
+ otg_dev = container_of(otg->phy, struct fsl_otg, phy);
+ if (otg_dev != fsl_otg_dev)
return -ENODEV;
otg_dev->fsm.b_bus_req = 1;
@@ -675,9 +685,13 @@ static int fsl_otg_start_srp(struct usb_otg *otg)
/* A_host suspend will call this function to start hnp */
static int fsl_otg_start_hnp(struct usb_otg *otg)
{
- struct fsl_otg *otg_dev = container_of(otg->phy, struct fsl_otg, phy);
+ struct fsl_otg *otg_dev;
+
+ if (!otg)
+ return -ENODEV;
- if (!otg || otg_dev != fsl_otg_dev)
+ otg_dev = container_of(otg->phy, struct fsl_otg, phy);
+ if (otg_dev != fsl_otg_dev)
return -ENODEV;
DBG("start_hnp...n");
diff --git a/drivers/usb/otg/mxs-phy.c b/drivers/usb/otg/mxs-phy.c
index c1a67cb8e244..88db976647cf 100644
--- a/drivers/usb/otg/mxs-phy.c
+++ b/drivers/usb/otg/mxs-phy.c
@@ -20,6 +20,7 @@
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
+#include <linux/workqueue.h>
#define DRIVER_NAME "mxs_phy"
@@ -34,9 +35,16 @@
#define BM_USBPHY_CTRL_ENUTMILEVEL2 BIT(14)
#define BM_USBPHY_CTRL_ENHOSTDISCONDETECT BIT(1)
+/*
+ * Amount of delay in miliseconds to safely enable ENHOSTDISCONDETECT bit
+ * so that connection and reset processing can be completed for the root hub.
+ */
+#define MXY_PHY_ENHOSTDISCONDETECT_DELAY 250
+
struct mxs_phy {
struct usb_phy phy;
struct clk *clk;
+ struct delayed_work enhostdiscondetect_work;
};
#define to_mxs_phy(p) container_of((p), struct mxs_phy, phy)
@@ -62,6 +70,7 @@ static int mxs_phy_init(struct usb_phy *phy)
clk_prepare_enable(mxs_phy->clk);
mxs_phy_hw_init(mxs_phy);
+ INIT_DELAYED_WORK(&mxs_phy->enhostdiscondetect_work, NULL);
return 0;
}
@@ -76,13 +85,34 @@ static void mxs_phy_shutdown(struct usb_phy *phy)
clk_disable_unprepare(mxs_phy->clk);
}
+static void mxs_phy_enhostdiscondetect_delay(struct work_struct *ws)
+{
+ struct mxs_phy *mxs_phy = container_of(ws, struct mxs_phy,
+ enhostdiscondetect_work.work);
+
+ /* Enable HOSTDISCONDETECT after delay. */
+ dev_dbg(mxs_phy->phy.dev, "Setting ENHOSTDISCONDETECT\n");
+ writel_relaxed(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,
+ mxs_phy->phy.io_priv + HW_USBPHY_CTRL_SET);
+}
+
static int mxs_phy_on_connect(struct usb_phy *phy, int port)
{
+ struct mxs_phy *mxs_phy = to_mxs_phy(phy);
+
dev_dbg(phy->dev, "Connect on port %d\n", port);
- mxs_phy_hw_init(to_mxs_phy(phy));
- writel_relaxed(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,
- phy->io_priv + HW_USBPHY_CTRL_SET);
+ mxs_phy_hw_init(mxs_phy);
+
+ /*
+ * Delay enabling ENHOSTDISCONDETECT so that connection and
+ * reset processing can be completed for the root hub.
+ */
+ dev_dbg(phy->dev, "Delaying setting ENHOSTDISCONDETECT\n");
+ PREPARE_DELAYED_WORK(&mxs_phy->enhostdiscondetect_work,
+ mxs_phy_enhostdiscondetect_delay);
+ schedule_delayed_work(&mxs_phy->enhostdiscondetect_work,
+ msecs_to_jiffies(MXY_PHY_ENHOSTDISCONDETECT_DELAY));
return 0;
}
@@ -91,6 +121,8 @@ static int mxs_phy_on_disconnect(struct usb_phy *phy, int port)
{
dev_dbg(phy->dev, "Disconnect on port %d\n", port);
+ /* No need to delay before clearing ENHOSTDISCONDETECT. */
+ dev_dbg(phy->dev, "Clearing ENHOSTDISCONDETECT\n");
writel_relaxed(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,
phy->io_priv + HW_USBPHY_CTRL_CLR);
diff --git a/drivers/usb/otg/nop-usb-xceiv.c b/drivers/usb/otg/nop-usb-xceiv.c
index 803f958f4133..e52e35e7adaf 100644
--- a/drivers/usb/otg/nop-usb-xceiv.c
+++ b/drivers/usb/otg/nop-usb-xceiv.c
@@ -30,6 +30,7 @@
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/usb/otg.h>
+#include <linux/usb/nop-usb-xceiv.h>
#include <linux/slab.h>
struct nop_usb_xceiv {
@@ -94,7 +95,9 @@ static int nop_set_host(struct usb_otg *otg, struct usb_bus *host)
static int __devinit nop_usb_xceiv_probe(struct platform_device *pdev)
{
+ struct nop_usb_xceiv_platform_data *pdata = pdev->dev.platform_data;
struct nop_usb_xceiv *nop;
+ enum usb_phy_type type = USB_PHY_TYPE_USB2;
int err;
nop = kzalloc(sizeof *nop, GFP_KERNEL);
@@ -107,6 +110,9 @@ static int __devinit nop_usb_xceiv_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ if (pdata)
+ type = pdata->type;
+
nop->dev = &pdev->dev;
nop->phy.dev = nop->dev;
nop->phy.label = "nop-xceiv";
@@ -117,7 +123,7 @@ static int __devinit nop_usb_xceiv_probe(struct platform_device *pdev)
nop->phy.otg->set_host = nop_set_host;
nop->phy.otg->set_peripheral = nop_set_peripheral;
- err = usb_add_phy(&nop->phy, USB_PHY_TYPE_USB2);
+ err = usb_add_phy(&nop->phy, type);
if (err) {
dev_err(&pdev->dev, "can't register transceiver, err: %d\n",
err);
diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c
index 1bf60a22595c..a30c04115115 100644
--- a/drivers/usb/otg/otg.c
+++ b/drivers/usb/otg/otg.c
@@ -159,7 +159,7 @@ int usb_add_phy(struct usb_phy *x, enum usb_phy_type type)
unsigned long flags;
struct usb_phy *phy;
- if (x && x->type != USB_PHY_TYPE_UNDEFINED) {
+ if (x->type != USB_PHY_TYPE_UNDEFINED) {
dev_err(x->dev, "not accepting initialized PHY %s\n", x->label);
return -EINVAL;
}
diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c
index 523cad5bfea9..f0d2e7530cfe 100644
--- a/drivers/usb/otg/twl4030-usb.c
+++ b/drivers/usb/otg/twl4030-usb.c
@@ -585,23 +585,28 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev)
struct twl4030_usb *twl;
int status, err;
struct usb_otg *otg;
-
- if (!pdata) {
- dev_dbg(&pdev->dev, "platform_data not available\n");
- return -EINVAL;
- }
+ struct device_node *np = pdev->dev.of_node;
twl = devm_kzalloc(&pdev->dev, sizeof *twl, GFP_KERNEL);
if (!twl)
return -ENOMEM;
+ if (np)
+ of_property_read_u32(np, "usb_mode",
+ (enum twl4030_usb_mode *)&twl->usb_mode);
+ else if (pdata)
+ twl->usb_mode = pdata->usb_mode;
+ else {
+ dev_err(&pdev->dev, "twl4030 initialized without pdata\n");
+ return -EINVAL;
+ }
+
otg = devm_kzalloc(&pdev->dev, sizeof *otg, GFP_KERNEL);
if (!otg)
return -ENOMEM;
twl->dev = &pdev->dev;
twl->irq = platform_get_irq(pdev, 0);
- twl->usb_mode = pdata->usb_mode;
twl->vbus_supplied = false;
twl->asleep = 1;
twl->linkstat = OMAP_MUSB_UNKNOWN;
@@ -690,12 +695,21 @@ static int __exit twl4030_usb_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_OF
+static const struct of_device_id twl4030_usb_id_table[] = {
+ { .compatible = "ti,twl4030-usb" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, twl4030_usb_id_table);
+#endif
+
static struct platform_driver twl4030_usb_driver = {
.probe = twl4030_usb_probe,
.remove = __exit_p(twl4030_usb_remove),
.driver = {
.name = "twl4030_usb",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(twl4030_usb_id_table),
},
};
diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c
index 6907d8df7a27..fcadef7864f1 100644
--- a/drivers/usb/otg/twl6030-usb.c
+++ b/drivers/usb/otg/twl6030-usb.c
@@ -25,8 +25,9 @@
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/io.h>
-#include <linux/usb/otg.h>
#include <linux/usb/musb-omap.h>
+#include <linux/usb/phy_companion.h>
+#include <linux/usb/omap_usb.h>
#include <linux/i2c/twl.h>
#include <linux/regulator/consumer.h>
#include <linux/err.h>
@@ -87,7 +88,7 @@
#define VBUS_DET BIT(2)
struct twl6030_usb {
- struct usb_phy phy;
+ struct phy_companion comparator;
struct device *dev;
/* for vbus reporting with irqs disabled */
@@ -104,10 +105,10 @@ struct twl6030_usb {
u8 asleep;
bool irq_enabled;
bool vbus_enable;
- unsigned long features;
+ const char *regulator;
};
-#define phy_to_twl(x) container_of((x), struct twl6030_usb, phy)
+#define comparator_to_twl(x) container_of((x), struct twl6030_usb, comparator)
/*-------------------------------------------------------------------------*/
@@ -137,50 +138,9 @@ static inline u8 twl6030_readb(struct twl6030_usb *twl, u8 module, u8 address)
return ret;
}
-static int twl6030_phy_init(struct usb_phy *x)
+static int twl6030_start_srp(struct phy_companion *comparator)
{
- struct twl6030_usb *twl;
- struct device *dev;
- struct twl4030_usb_data *pdata;
-
- twl = phy_to_twl(x);
- dev = twl->dev;
- pdata = dev->platform_data;
-
- if (twl->linkstat == OMAP_MUSB_ID_GROUND)
- pdata->phy_power(twl->dev, 1, 1);
- else
- pdata->phy_power(twl->dev, 0, 1);
-
- return 0;
-}
-
-static void twl6030_phy_shutdown(struct usb_phy *x)
-{
- struct twl6030_usb *twl;
- struct device *dev;
- struct twl4030_usb_data *pdata;
-
- twl = phy_to_twl(x);
- dev = twl->dev;
- pdata = dev->platform_data;
- pdata->phy_power(twl->dev, 0, 0);
-}
-
-static int twl6030_phy_suspend(struct usb_phy *x, int suspend)
-{
- struct twl6030_usb *twl = phy_to_twl(x);
- struct device *dev = twl->dev;
- struct twl4030_usb_data *pdata = dev->platform_data;
-
- pdata->phy_suspend(dev, suspend);
-
- return 0;
-}
-
-static int twl6030_start_srp(struct usb_otg *otg)
-{
- struct twl6030_usb *twl = phy_to_twl(otg->phy);
+ struct twl6030_usb *twl = comparator_to_twl(comparator);
twl6030_writeb(twl, TWL_MODULE_USB, 0x24, USB_VBUS_CTRL_SET);
twl6030_writeb(twl, TWL_MODULE_USB, 0x84, USB_VBUS_CTRL_SET);
@@ -193,13 +153,6 @@ static int twl6030_start_srp(struct usb_otg *otg)
static int twl6030_usb_ldo_init(struct twl6030_usb *twl)
{
- char *regulator_name;
-
- if (twl->features & TWL6025_SUBCLASS)
- regulator_name = "ldousb";
- else
- regulator_name = "vusb";
-
/* Set to OTG_REV 1.3 and turn on the ID_WAKEUP_COMP */
twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x1, TWL6030_BACKUP_REG);
@@ -209,7 +162,7 @@ static int twl6030_usb_ldo_init(struct twl6030_usb *twl)
/* Program MISC2 register and set bit VUSB_IN_VBAT */
twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x10, TWL6030_MISC2);
- twl->usb3v3 = regulator_get(twl->dev, regulator_name);
+ twl->usb3v3 = regulator_get(twl->dev, twl->regulator);
if (IS_ERR(twl->usb3v3))
return -ENODEV;
@@ -313,23 +266,8 @@ static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl)
return IRQ_HANDLED;
}
-static int twl6030_set_peripheral(struct usb_otg *otg,
- struct usb_gadget *gadget)
-{
- if (!otg)
- return -ENODEV;
-
- otg->gadget = gadget;
- if (!gadget)
- otg->phy->state = OTG_STATE_UNDEFINED;
-
- return 0;
-}
-
-static int twl6030_enable_irq(struct usb_phy *x)
+static int twl6030_enable_irq(struct twl6030_usb *twl)
{
- struct twl6030_usb *twl = phy_to_twl(x);
-
twl6030_writeb(twl, TWL_MODULE_USB, 0x1, USB_ID_INT_EN_HI_SET);
twl6030_interrupt_unmask(0x05, REG_INT_MSK_LINE_C);
twl6030_interrupt_unmask(0x05, REG_INT_MSK_STS_C);
@@ -362,9 +300,9 @@ static void otg_set_vbus_work(struct work_struct *data)
CHARGERUSB_CTRL1);
}
-static int twl6030_set_vbus(struct usb_otg *otg, bool enabled)
+static int twl6030_set_vbus(struct phy_companion *comparator, bool enabled)
{
- struct twl6030_usb *twl = phy_to_twl(otg->phy);
+ struct twl6030_usb *twl = comparator_to_twl(comparator);
twl->vbus_enable = enabled;
schedule_work(&twl->set_vbus_work);
@@ -372,52 +310,44 @@ static int twl6030_set_vbus(struct usb_otg *otg, bool enabled)
return 0;
}
-static int twl6030_set_host(struct usb_otg *otg, struct usb_bus *host)
-{
- if (!otg)
- return -ENODEV;
-
- otg->host = host;
- if (!host)
- otg->phy->state = OTG_STATE_UNDEFINED;
- return 0;
-}
-
static int __devinit twl6030_usb_probe(struct platform_device *pdev)
{
+ u32 ret;
struct twl6030_usb *twl;
int status, err;
- struct twl4030_usb_data *pdata;
- struct usb_otg *otg;
- struct device *dev = &pdev->dev;
- pdata = dev->platform_data;
+ struct device_node *np = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct twl4030_usb_data *pdata = dev->platform_data;
twl = devm_kzalloc(dev, sizeof *twl, GFP_KERNEL);
if (!twl)
return -ENOMEM;
- otg = devm_kzalloc(dev, sizeof *otg, GFP_KERNEL);
- if (!otg)
- return -ENOMEM;
-
twl->dev = &pdev->dev;
twl->irq1 = platform_get_irq(pdev, 0);
twl->irq2 = platform_get_irq(pdev, 1);
- twl->features = pdata->features;
twl->linkstat = OMAP_MUSB_UNKNOWN;
- twl->phy.dev = twl->dev;
- twl->phy.label = "twl6030";
- twl->phy.otg = otg;
- twl->phy.init = twl6030_phy_init;
- twl->phy.shutdown = twl6030_phy_shutdown;
- twl->phy.set_suspend = twl6030_phy_suspend;
+ twl->comparator.set_vbus = twl6030_set_vbus;
+ twl->comparator.start_srp = twl6030_start_srp;
+
+ ret = omap_usb2_set_comparator(&twl->comparator);
+ if (ret == -ENODEV) {
+ dev_info(&pdev->dev, "phy not ready, deferring probe");
+ return -EPROBE_DEFER;
+ }
- otg->phy = &twl->phy;
- otg->set_host = twl6030_set_host;
- otg->set_peripheral = twl6030_set_peripheral;
- otg->set_vbus = twl6030_set_vbus;
- otg->start_srp = twl6030_start_srp;
+ if (np) {
+ twl->regulator = "usb";
+ } else if (pdata) {
+ if (pdata->features & TWL6025_SUBCLASS)
+ twl->regulator = "ldousb";
+ else
+ twl->regulator = "vusb";
+ } else {
+ dev_err(&pdev->dev, "twl6030 initialized without pdata\n");
+ return -EINVAL;
+ }
/* init spinlock for workqueue */
spin_lock_init(&twl->lock);
@@ -427,7 +357,6 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "ldo init failed\n");
return err;
}
- usb_add_phy(&twl->phy, USB_PHY_TYPE_USB2);
platform_set_drvdata(pdev, twl);
if (device_create_file(&pdev->dev, &dev_attr_vbus))
@@ -458,9 +387,7 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev)
}
twl->asleep = 0;
- pdata->phy_init(dev);
- twl6030_phy_suspend(&twl->phy, 0);
- twl6030_enable_irq(&twl->phy);
+ twl6030_enable_irq(twl);
dev_info(&pdev->dev, "Initialized TWL6030 USB module\n");
return 0;
@@ -470,10 +397,6 @@ static int __exit twl6030_usb_remove(struct platform_device *pdev)
{
struct twl6030_usb *twl = platform_get_drvdata(pdev);
- struct twl4030_usb_data *pdata;
- struct device *dev = &pdev->dev;
- pdata = dev->platform_data;
-
twl6030_interrupt_mask(TWL6030_USBOTG_INT_MASK,
REG_INT_MSK_LINE_C);
twl6030_interrupt_mask(TWL6030_USBOTG_INT_MASK,
@@ -481,19 +404,27 @@ static int __exit twl6030_usb_remove(struct platform_device *pdev)
free_irq(twl->irq1, twl);
free_irq(twl->irq2, twl);
regulator_put(twl->usb3v3);
- pdata->phy_exit(twl->dev);
device_remove_file(twl->dev, &dev_attr_vbus);
cancel_work_sync(&twl->set_vbus_work);
return 0;
}
+#ifdef CONFIG_OF
+static const struct of_device_id twl6030_usb_id_table[] = {
+ { .compatible = "ti,twl6030-usb" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, twl6030_usb_id_table);
+#endif
+
static struct platform_driver twl6030_usb_driver = {
.probe = twl6030_usb_probe,
.remove = __exit_p(twl6030_usb_remove),
.driver = {
.name = "twl6030_usb",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(twl6030_usb_id_table),
},
};
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
index e7cf84f0751a..63c339b3e676 100644
--- a/drivers/usb/phy/Kconfig
+++ b/drivers/usb/phy/Kconfig
@@ -4,6 +4,15 @@
comment "USB Physical Layer drivers"
depends on USB || USB_GADGET
+config OMAP_USB2
+ tristate "OMAP USB2 PHY Driver"
+ select USB_OTG_UTILS
+ help
+ Enable this to support the transceiver that is part of SOC. This
+ driver takes care of all the PHY functionality apart from comparator.
+ The USB OTG controller communicates with the comparator using this
+ driver.
+
config USB_ISP1301
tristate "NXP ISP1301 USB transceiver support"
depends on USB || USB_GADGET
@@ -15,3 +24,11 @@ config USB_ISP1301
To compile this driver as a module, choose M here: the
module will be called isp1301.
+
+config MV_U3D_PHY
+ bool "Marvell USB 3.0 PHY controller Driver"
+ depends on USB_MV_U3D
+ select USB_OTG_UTILS
+ help
+ Enable this to support Marvell USB 3.0 phy controller for Marvell
+ SoC.
diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile
index eca095b1a890..b069f29f1225 100644
--- a/drivers/usb/phy/Makefile
+++ b/drivers/usb/phy/Makefile
@@ -4,4 +4,7 @@
ccflags-$(CONFIG_USB_DEBUG) := -DDEBUG
+obj-$(CONFIG_OMAP_USB2) += omap-usb2.o
obj-$(CONFIG_USB_ISP1301) += isp1301.o
+obj-$(CONFIG_MV_U3D_PHY) += mv_u3d_phy.o
+obj-$(CONFIG_USB_EHCI_TEGRA) += tegra_usb_phy.o
diff --git a/drivers/usb/phy/isp1301.c b/drivers/usb/phy/isp1301.c
index b19f4932a037..18dbf7e37607 100644
--- a/drivers/usb/phy/isp1301.c
+++ b/drivers/usb/phy/isp1301.c
@@ -15,12 +15,6 @@
#define DRV_NAME "isp1301"
-#define ISP1301_I2C_ADDR 0x2C
-
-static const unsigned short normal_i2c[] = {
- ISP1301_I2C_ADDR, ISP1301_I2C_ADDR + 1, I2C_CLIENT_END
-};
-
static const struct i2c_device_id isp1301_id[] = {
{ "isp1301", 0 },
{ }
diff --git a/drivers/usb/phy/mv_u3d_phy.c b/drivers/usb/phy/mv_u3d_phy.c
new file mode 100644
index 000000000000..9f1c5d3c60ec
--- /dev/null
+++ b/drivers/usb/phy/mv_u3d_phy.c
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2011 Marvell International Ltd. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/usb/otg.h>
+#include <linux/platform_data/mv_usb.h>
+
+#include "mv_u3d_phy.h"
+
+/*
+ * struct mv_u3d_phy - transceiver driver state
+ * @phy: transceiver structure
+ * @dev: The parent device supplied to the probe function
+ * @clk: usb phy clock
+ * @base: usb phy register memory base
+ */
+struct mv_u3d_phy {
+ struct usb_phy phy;
+ struct mv_usb_platform_data *plat;
+ struct device *dev;
+ struct clk *clk;
+ void __iomem *base;
+};
+
+static u32 mv_u3d_phy_read(void __iomem *base, u32 reg)
+{
+ void __iomem *addr, *data;
+
+ addr = base;
+ data = base + 0x4;
+
+ writel_relaxed(reg, addr);
+ return readl_relaxed(data);
+}
+
+static void mv_u3d_phy_set(void __iomem *base, u32 reg, u32 value)
+{
+ void __iomem *addr, *data;
+ u32 tmp;
+
+ addr = base;
+ data = base + 0x4;
+
+ writel_relaxed(reg, addr);
+ tmp = readl_relaxed(data);
+ tmp |= value;
+ writel_relaxed(tmp, data);
+}
+
+static void mv_u3d_phy_clear(void __iomem *base, u32 reg, u32 value)
+{
+ void __iomem *addr, *data;
+ u32 tmp;
+
+ addr = base;
+ data = base + 0x4;
+
+ writel_relaxed(reg, addr);
+ tmp = readl_relaxed(data);
+ tmp &= ~value;
+ writel_relaxed(tmp, data);
+}
+
+static void mv_u3d_phy_write(void __iomem *base, u32 reg, u32 value)
+{
+ void __iomem *addr, *data;
+
+ addr = base;
+ data = base + 0x4;
+
+ writel_relaxed(reg, addr);
+ writel_relaxed(value, data);
+}
+
+void mv_u3d_phy_shutdown(struct usb_phy *phy)
+{
+ struct mv_u3d_phy *mv_u3d_phy;
+ void __iomem *base;
+ u32 val;
+
+ mv_u3d_phy = container_of(phy, struct mv_u3d_phy, phy);
+ base = mv_u3d_phy->base;
+
+ /* Power down Reference Analog current, bit 15
+ * Power down PLL, bit 14
+ * Power down Receiver, bit 13
+ * Power down Transmitter, bit 12
+ * of USB3_POWER_PLL_CONTROL register
+ */
+ val = mv_u3d_phy_read(base, USB3_POWER_PLL_CONTROL);
+ val &= ~(USB3_POWER_PLL_CONTROL_PU);
+ mv_u3d_phy_write(base, USB3_POWER_PLL_CONTROL, val);
+
+ if (mv_u3d_phy->clk)
+ clk_disable(mv_u3d_phy->clk);
+}
+
+static int mv_u3d_phy_init(struct usb_phy *phy)
+{
+ struct mv_u3d_phy *mv_u3d_phy;
+ void __iomem *base;
+ u32 val, count;
+
+ /* enable usb3 phy */
+ mv_u3d_phy = container_of(phy, struct mv_u3d_phy, phy);
+
+ if (mv_u3d_phy->clk)
+ clk_enable(mv_u3d_phy->clk);
+
+ base = mv_u3d_phy->base;
+
+ val = mv_u3d_phy_read(base, USB3_POWER_PLL_CONTROL);
+ val &= ~(USB3_POWER_PLL_CONTROL_PU_MASK);
+ val |= 0xF << USB3_POWER_PLL_CONTROL_PU_SHIFT;
+ mv_u3d_phy_write(base, USB3_POWER_PLL_CONTROL, val);
+ udelay(100);
+
+ mv_u3d_phy_write(base, USB3_RESET_CONTROL,
+ USB3_RESET_CONTROL_RESET_PIPE);
+ udelay(100);
+
+ mv_u3d_phy_write(base, USB3_RESET_CONTROL,
+ USB3_RESET_CONTROL_RESET_PIPE
+ | USB3_RESET_CONTROL_RESET_PHY);
+ udelay(100);
+
+ val = mv_u3d_phy_read(base, USB3_POWER_PLL_CONTROL);
+ val &= ~(USB3_POWER_PLL_CONTROL_REF_FREF_SEL_MASK
+ | USB3_POWER_PLL_CONTROL_PHY_MODE_MASK);
+ val |= (USB3_PLL_25MHZ << USB3_POWER_PLL_CONTROL_REF_FREF_SEL_SHIFT)
+ | (0x5 << USB3_POWER_PLL_CONTROL_PHY_MODE_SHIFT);
+ mv_u3d_phy_write(base, USB3_POWER_PLL_CONTROL, val);
+ udelay(100);
+
+ mv_u3d_phy_clear(base, USB3_KVCO_CALI_CONTROL,
+ USB3_KVCO_CALI_CONTROL_USE_MAX_PLL_RATE_MASK);
+ udelay(100);
+
+ val = mv_u3d_phy_read(base, USB3_SQUELCH_FFE);
+ val &= ~(USB3_SQUELCH_FFE_FFE_CAP_SEL_MASK
+ | USB3_SQUELCH_FFE_FFE_RES_SEL_MASK
+ | USB3_SQUELCH_FFE_SQ_THRESH_IN_MASK);
+ val |= ((0xD << USB3_SQUELCH_FFE_FFE_CAP_SEL_SHIFT)
+ | (0x7 << USB3_SQUELCH_FFE_FFE_RES_SEL_SHIFT)
+ | (0x8 << USB3_SQUELCH_FFE_SQ_THRESH_IN_SHIFT));
+ mv_u3d_phy_write(base, USB3_SQUELCH_FFE, val);
+ udelay(100);
+
+ val = mv_u3d_phy_read(base, USB3_GEN1_SET0);
+ val &= ~USB3_GEN1_SET0_G1_TX_SLEW_CTRL_EN_MASK;
+ val |= 1 << USB3_GEN1_SET0_G1_TX_EMPH_EN_SHIFT;
+ mv_u3d_phy_write(base, USB3_GEN1_SET0, val);
+ udelay(100);
+
+ val = mv_u3d_phy_read(base, USB3_GEN2_SET0);
+ val &= ~(USB3_GEN2_SET0_G2_TX_AMP_MASK
+ | USB3_GEN2_SET0_G2_TX_EMPH_AMP_MASK
+ | USB3_GEN2_SET0_G2_TX_SLEW_CTRL_EN_MASK);
+ val |= ((0x14 << USB3_GEN2_SET0_G2_TX_AMP_SHIFT)
+ | (1 << USB3_GEN2_SET0_G2_TX_AMP_ADJ_SHIFT)
+ | (0xA << USB3_GEN2_SET0_G2_TX_EMPH_AMP_SHIFT)
+ | (1 << USB3_GEN2_SET0_G2_TX_EMPH_EN_SHIFT));
+ mv_u3d_phy_write(base, USB3_GEN2_SET0, val);
+ udelay(100);
+
+ mv_u3d_phy_read(base, USB3_TX_EMPPH);
+ val &= ~(USB3_TX_EMPPH_AMP_MASK
+ | USB3_TX_EMPPH_EN_MASK
+ | USB3_TX_EMPPH_AMP_FORCE_MASK
+ | USB3_TX_EMPPH_PAR1_MASK
+ | USB3_TX_EMPPH_PAR2_MASK);
+ val |= ((0xB << USB3_TX_EMPPH_AMP_SHIFT)
+ | (1 << USB3_TX_EMPPH_EN_SHIFT)
+ | (1 << USB3_TX_EMPPH_AMP_FORCE_SHIFT)
+ | (0x1C << USB3_TX_EMPPH_PAR1_SHIFT)
+ | (1 << USB3_TX_EMPPH_PAR2_SHIFT));
+
+ mv_u3d_phy_write(base, USB3_TX_EMPPH, val);
+ udelay(100);
+
+ val = mv_u3d_phy_read(base, USB3_GEN2_SET1);
+ val &= ~(USB3_GEN2_SET1_G2_RX_SELMUPI_MASK
+ | USB3_GEN2_SET1_G2_RX_SELMUPF_MASK
+ | USB3_GEN2_SET1_G2_RX_SELMUFI_MASK
+ | USB3_GEN2_SET1_G2_RX_SELMUFF_MASK);
+ val |= ((1 << USB3_GEN2_SET1_G2_RX_SELMUPI_SHIFT)
+ | (1 << USB3_GEN2_SET1_G2_RX_SELMUPF_SHIFT)
+ | (1 << USB3_GEN2_SET1_G2_RX_SELMUFI_SHIFT)
+ | (1 << USB3_GEN2_SET1_G2_RX_SELMUFF_SHIFT));
+ mv_u3d_phy_write(base, USB3_GEN2_SET1, val);
+ udelay(100);
+
+ val = mv_u3d_phy_read(base, USB3_DIGITAL_LOOPBACK_EN);
+ val &= ~USB3_DIGITAL_LOOPBACK_EN_SEL_BITS_MASK;
+ val |= 1 << USB3_DIGITAL_LOOPBACK_EN_SEL_BITS_SHIFT;
+ mv_u3d_phy_write(base, USB3_DIGITAL_LOOPBACK_EN, val);
+ udelay(100);
+
+ val = mv_u3d_phy_read(base, USB3_IMPEDANCE_TX_SSC);
+ val &= ~USB3_IMPEDANCE_TX_SSC_SSC_AMP_MASK;
+ val |= 0xC << USB3_IMPEDANCE_TX_SSC_SSC_AMP_SHIFT;
+ mv_u3d_phy_write(base, USB3_IMPEDANCE_TX_SSC, val);
+ udelay(100);
+
+ val = mv_u3d_phy_read(base, USB3_IMPEDANCE_CALI_CTRL);
+ val &= ~USB3_IMPEDANCE_CALI_CTRL_IMP_CAL_THR_MASK;
+ val |= 0x4 << USB3_IMPEDANCE_CALI_CTRL_IMP_CAL_THR_SHIFT;
+ mv_u3d_phy_write(base, USB3_IMPEDANCE_CALI_CTRL, val);
+ udelay(100);
+
+ val = mv_u3d_phy_read(base, USB3_PHY_ISOLATION_MODE);
+ val &= ~(USB3_PHY_ISOLATION_MODE_PHY_GEN_RX_MASK
+ | USB3_PHY_ISOLATION_MODE_PHY_GEN_TX_MASK
+ | USB3_PHY_ISOLATION_MODE_TX_DRV_IDLE_MASK);
+ val |= ((1 << USB3_PHY_ISOLATION_MODE_PHY_GEN_RX_SHIFT)
+ | (1 << USB3_PHY_ISOLATION_MODE_PHY_GEN_TX_SHIFT));
+ mv_u3d_phy_write(base, USB3_PHY_ISOLATION_MODE, val);
+ udelay(100);
+
+ val = mv_u3d_phy_read(base, USB3_TXDETRX);
+ val &= ~(USB3_TXDETRX_VTHSEL_MASK);
+ val |= 0x1 << USB3_TXDETRX_VTHSEL_SHIFT;
+ mv_u3d_phy_write(base, USB3_TXDETRX, val);
+ udelay(100);
+
+ dev_dbg(mv_u3d_phy->dev, "start calibration\n");
+
+calstart:
+ /* Perform Manual Calibration */
+ mv_u3d_phy_set(base, USB3_KVCO_CALI_CONTROL,
+ 1 << USB3_KVCO_CALI_CONTROL_CAL_START_SHIFT);
+
+ mdelay(1);
+
+ count = 0;
+ while (1) {
+ val = mv_u3d_phy_read(base, USB3_KVCO_CALI_CONTROL);
+ if (val & (1 << USB3_KVCO_CALI_CONTROL_CAL_DONE_SHIFT))
+ break;
+ else if (count > 50) {
+ dev_dbg(mv_u3d_phy->dev, "calibration failure, retry...\n");
+ goto calstart;
+ }
+ count++;
+ mdelay(1);
+ }
+
+ /* active PIPE interface */
+ mv_u3d_phy_write(base, USB3_PIPE_SM_CTRL,
+ 1 << USB3_PIPE_SM_CTRL_PHY_INIT_DONE);
+
+ return 0;
+}
+
+static int __devinit mv_u3d_phy_probe(struct platform_device *pdev)
+{
+ struct mv_u3d_phy *mv_u3d_phy;
+ struct mv_usb_platform_data *pdata;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ void __iomem *phy_base;
+ int ret;
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(&pdev->dev, "%s: no platform data defined\n", __func__);
+ return -EINVAL;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "missing mem resource\n");
+ return -ENODEV;
+ }
+
+ phy_base = devm_request_and_ioremap(dev, res);
+ if (!phy_base) {
+ dev_err(dev, "%s: register mapping failed\n", __func__);
+ return -ENXIO;
+ }
+
+ mv_u3d_phy = devm_kzalloc(dev, sizeof(*mv_u3d_phy), GFP_KERNEL);
+ if (!mv_u3d_phy)
+ return -ENOMEM;
+
+ mv_u3d_phy->dev = &pdev->dev;
+ mv_u3d_phy->plat = pdata;
+ mv_u3d_phy->base = phy_base;
+ mv_u3d_phy->phy.dev = mv_u3d_phy->dev;
+ mv_u3d_phy->phy.label = "mv-u3d-phy";
+ mv_u3d_phy->phy.init = mv_u3d_phy_init;
+ mv_u3d_phy->phy.shutdown = mv_u3d_phy_shutdown;
+
+ ret = usb_add_phy(&mv_u3d_phy->phy, USB_PHY_TYPE_USB3);
+ if (ret)
+ goto err;
+
+ if (!mv_u3d_phy->clk)
+ mv_u3d_phy->clk = clk_get(mv_u3d_phy->dev, "u3dphy");
+
+ platform_set_drvdata(pdev, mv_u3d_phy);
+
+ dev_info(&pdev->dev, "Initialized Marvell USB 3.0 PHY\n");
+err:
+ return ret;
+}
+
+static int __exit mv_u3d_phy_remove(struct platform_device *pdev)
+{
+ struct mv_u3d_phy *mv_u3d_phy = platform_get_drvdata(pdev);
+
+ usb_remove_phy(&mv_u3d_phy->phy);
+
+ if (mv_u3d_phy->clk) {
+ clk_put(mv_u3d_phy->clk);
+ mv_u3d_phy->clk = NULL;
+ }
+
+ return 0;
+}
+
+static struct platform_driver mv_u3d_phy_driver = {
+ .probe = mv_u3d_phy_probe,
+ .remove = __devexit_p(mv_u3d_phy_remove),
+ .driver = {
+ .name = "mv-u3d-phy",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(mv_u3d_phy_driver);
+MODULE_DESCRIPTION("Marvell USB 3.0 PHY controller");
+MODULE_AUTHOR("Yu Xu <yuxu@marvell.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:mv-u3d-phy");
diff --git a/drivers/usb/phy/mv_u3d_phy.h b/drivers/usb/phy/mv_u3d_phy.h
new file mode 100644
index 000000000000..2a658cb9a527
--- /dev/null
+++ b/drivers/usb/phy/mv_u3d_phy.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2011 Marvell International Ltd. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+#ifndef __MV_U3D_PHY_H
+#define __MV_U3D_PHY_H
+
+#define USB3_POWER_PLL_CONTROL 0x1
+#define USB3_KVCO_CALI_CONTROL 0x2
+#define USB3_IMPEDANCE_CALI_CTRL 0x3
+#define USB3_IMPEDANCE_TX_SSC 0x4
+#define USB3_SQUELCH_FFE 0x6
+#define USB3_GEN1_SET0 0xD
+#define USB3_GEN2_SET0 0xF
+#define USB3_GEN2_SET1 0x10
+#define USB3_DIGITAL_LOOPBACK_EN 0x23
+#define USB3_PHY_ISOLATION_MODE 0x26
+#define USB3_TXDETRX 0x48
+#define USB3_TX_EMPPH 0x5E
+#define USB3_RESET_CONTROL 0x90
+#define USB3_PIPE_SM_CTRL 0x91
+
+#define USB3_RESET_CONTROL_RESET_PIPE 0x1
+#define USB3_RESET_CONTROL_RESET_PHY 0x2
+
+#define USB3_POWER_PLL_CONTROL_REF_FREF_SEL_MASK (0x1F << 0)
+#define USB3_POWER_PLL_CONTROL_REF_FREF_SEL_SHIFT 0
+#define USB3_PLL_25MHZ 0x2
+#define USB3_PLL_26MHZ 0x5
+#define USB3_POWER_PLL_CONTROL_PHY_MODE_MASK (0x7 << 5)
+#define USB3_POWER_PLL_CONTROL_PHY_MODE_SHIFT 5
+#define USB3_POWER_PLL_CONTROL_PU_MASK (0xF << 12)
+#define USB3_POWER_PLL_CONTROL_PU_SHIFT 12
+#define USB3_POWER_PLL_CONTROL_PU (0xF << 12)
+
+#define USB3_KVCO_CALI_CONTROL_USE_MAX_PLL_RATE_MASK (0x1 << 12)
+#define USB3_KVCO_CALI_CONTROL_USE_MAX_PLL_RATE_SHIFT 12
+#define USB3_KVCO_CALI_CONTROL_CAL_DONE_SHIFT 14
+#define USB3_KVCO_CALI_CONTROL_CAL_START_SHIFT 15
+
+#define USB3_SQUELCH_FFE_FFE_CAP_SEL_MASK 0xF
+#define USB3_SQUELCH_FFE_FFE_CAP_SEL_SHIFT 0
+#define USB3_SQUELCH_FFE_FFE_RES_SEL_MASK (0x7 << 4)
+#define USB3_SQUELCH_FFE_FFE_RES_SEL_SHIFT 4
+#define USB3_SQUELCH_FFE_SQ_THRESH_IN_MASK (0x1F << 8)
+#define USB3_SQUELCH_FFE_SQ_THRESH_IN_SHIFT 8
+
+#define USB3_GEN1_SET0_G1_TX_SLEW_CTRL_EN_MASK (0x1 << 15)
+#define USB3_GEN1_SET0_G1_TX_EMPH_EN_SHIFT 11
+
+#define USB3_GEN2_SET0_G2_TX_AMP_MASK (0x1F << 1)
+#define USB3_GEN2_SET0_G2_TX_AMP_SHIFT 1
+#define USB3_GEN2_SET0_G2_TX_AMP_ADJ_SHIFT 6
+#define USB3_GEN2_SET0_G2_TX_EMPH_AMP_MASK (0xF << 7)
+#define USB3_GEN2_SET0_G2_TX_EMPH_AMP_SHIFT 7
+#define USB3_GEN2_SET0_G2_TX_EMPH_EN_MASK (0x1 << 11)
+#define USB3_GEN2_SET0_G2_TX_EMPH_EN_SHIFT 11
+#define USB3_GEN2_SET0_G2_TX_SLEW_CTRL_EN_MASK (0x1 << 15)
+#define USB3_GEN2_SET0_G2_TX_SLEW_CTRL_EN_SHIFT 15
+
+#define USB3_GEN2_SET1_G2_RX_SELMUPI_MASK (0x7 << 0)
+#define USB3_GEN2_SET1_G2_RX_SELMUPI_SHIFT 0
+#define USB3_GEN2_SET1_G2_RX_SELMUPF_MASK (0x7 << 3)
+#define USB3_GEN2_SET1_G2_RX_SELMUPF_SHIFT 3
+#define USB3_GEN2_SET1_G2_RX_SELMUFI_MASK (0x3 << 6)
+#define USB3_GEN2_SET1_G2_RX_SELMUFI_SHIFT 6
+#define USB3_GEN2_SET1_G2_RX_SELMUFF_MASK (0x3 << 8)
+#define USB3_GEN2_SET1_G2_RX_SELMUFF_SHIFT 8
+
+#define USB3_DIGITAL_LOOPBACK_EN_SEL_BITS_MASK (0x3 << 10)
+#define USB3_DIGITAL_LOOPBACK_EN_SEL_BITS_SHIFT 10
+
+#define USB3_IMPEDANCE_CALI_CTRL_IMP_CAL_THR_MASK (0x7 << 12)
+#define USB3_IMPEDANCE_CALI_CTRL_IMP_CAL_THR_SHIFT 12
+
+#define USB3_IMPEDANCE_TX_SSC_SSC_AMP_MASK (0x3F << 0)
+#define USB3_IMPEDANCE_TX_SSC_SSC_AMP_SHIFT 0
+
+#define USB3_PHY_ISOLATION_MODE_PHY_GEN_RX_MASK 0xF
+#define USB3_PHY_ISOLATION_MODE_PHY_GEN_RX_SHIFT 0
+#define USB3_PHY_ISOLATION_MODE_PHY_GEN_TX_MASK (0xF << 4)
+#define USB3_PHY_ISOLATION_MODE_PHY_GEN_TX_SHIFT 4
+#define USB3_PHY_ISOLATION_MODE_TX_DRV_IDLE_MASK (0x1 << 8)
+
+#define USB3_TXDETRX_VTHSEL_MASK (0x3 << 4)
+#define USB3_TXDETRX_VTHSEL_SHIFT 4
+
+#define USB3_TX_EMPPH_AMP_MASK (0xF << 0)
+#define USB3_TX_EMPPH_AMP_SHIFT 0
+#define USB3_TX_EMPPH_EN_MASK (0x1 << 6)
+#define USB3_TX_EMPPH_EN_SHIFT 6
+#define USB3_TX_EMPPH_AMP_FORCE_MASK (0x1 << 7)
+#define USB3_TX_EMPPH_AMP_FORCE_SHIFT 7
+#define USB3_TX_EMPPH_PAR1_MASK (0x1F << 8)
+#define USB3_TX_EMPPH_PAR1_SHIFT 8
+#define USB3_TX_EMPPH_PAR2_MASK (0x1 << 13)
+#define USB3_TX_EMPPH_PAR2_SHIFT 13
+
+#define USB3_PIPE_SM_CTRL_PHY_INIT_DONE 15
+
+#endif /* __MV_U3D_PHY_H */
diff --git a/drivers/usb/phy/omap-usb2.c b/drivers/usb/phy/omap-usb2.c
new file mode 100644
index 000000000000..15ab3d6f2e8c
--- /dev/null
+++ b/drivers/usb/phy/omap-usb2.c
@@ -0,0 +1,271 @@
+/*
+ * omap-usb2.c - USB PHY, talking to musb controller in OMAP.
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/io.h>
+#include <linux/usb/omap_usb.h>
+#include <linux/usb/phy_companion.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/pm_runtime.h>
+#include <linux/delay.h>
+
+/**
+ * omap_usb2_set_comparator - links the comparator present in the sytem with
+ * this phy
+ * @comparator - the companion phy(comparator) for this phy
+ *
+ * The phy companion driver should call this API passing the phy_companion
+ * filled with set_vbus and start_srp to be used by usb phy.
+ *
+ * For use by phy companion driver
+ */
+int omap_usb2_set_comparator(struct phy_companion *comparator)
+{
+ struct omap_usb *phy;
+ struct usb_phy *x = usb_get_phy(USB_PHY_TYPE_USB2);
+
+ if (IS_ERR(x))
+ return -ENODEV;
+
+ phy = phy_to_omapusb(x);
+ phy->comparator = comparator;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(omap_usb2_set_comparator);
+
+/**
+ * omap_usb_phy_power - power on/off the phy using control module reg
+ * @phy: struct omap_usb *
+ * @on: 0 or 1, based on powering on or off the PHY
+ *
+ * XXX: Remove this function once control module driver gets merged
+ */
+static void omap_usb_phy_power(struct omap_usb *phy, int on)
+{
+ u32 val;
+
+ if (on) {
+ val = readl(phy->control_dev);
+ if (val & PHY_PD) {
+ writel(~PHY_PD, phy->control_dev);
+ /* XXX: add proper documentation for this delay */
+ mdelay(200);
+ }
+ } else {
+ writel(PHY_PD, phy->control_dev);
+ }
+}
+
+static int omap_usb_set_vbus(struct usb_otg *otg, bool enabled)
+{
+ struct omap_usb *phy = phy_to_omapusb(otg->phy);
+
+ if (!phy->comparator)
+ return -ENODEV;
+
+ return phy->comparator->set_vbus(phy->comparator, enabled);
+}
+
+static int omap_usb_start_srp(struct usb_otg *otg)
+{
+ struct omap_usb *phy = phy_to_omapusb(otg->phy);
+
+ if (!phy->comparator)
+ return -ENODEV;
+
+ return phy->comparator->start_srp(phy->comparator);
+}
+
+static int omap_usb_set_host(struct usb_otg *otg, struct usb_bus *host)
+{
+ struct usb_phy *phy = otg->phy;
+
+ otg->host = host;
+ if (!host)
+ phy->state = OTG_STATE_UNDEFINED;
+
+ return 0;
+}
+
+static int omap_usb_set_peripheral(struct usb_otg *otg,
+ struct usb_gadget *gadget)
+{
+ struct usb_phy *phy = otg->phy;
+
+ otg->gadget = gadget;
+ if (!gadget)
+ phy->state = OTG_STATE_UNDEFINED;
+
+ return 0;
+}
+
+static int omap_usb2_suspend(struct usb_phy *x, int suspend)
+{
+ u32 ret;
+ struct omap_usb *phy = phy_to_omapusb(x);
+
+ if (suspend && !phy->is_suspended) {
+ omap_usb_phy_power(phy, 0);
+ pm_runtime_put_sync(phy->dev);
+ phy->is_suspended = 1;
+ } else if (!suspend && phy->is_suspended) {
+ ret = pm_runtime_get_sync(phy->dev);
+ if (ret < 0) {
+ dev_err(phy->dev, "get_sync failed with err %d\n",
+ ret);
+ return ret;
+ }
+ omap_usb_phy_power(phy, 1);
+ phy->is_suspended = 0;
+ }
+
+ return 0;
+}
+
+static int __devinit omap_usb2_probe(struct platform_device *pdev)
+{
+ struct omap_usb *phy;
+ struct usb_otg *otg;
+ struct resource *res;
+
+ phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
+ if (!phy) {
+ dev_err(&pdev->dev, "unable to allocate memory for USB2 PHY\n");
+ return -ENOMEM;
+ }
+
+ otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL);
+ if (!otg) {
+ dev_err(&pdev->dev, "unable to allocate memory for USB OTG\n");
+ return -ENOMEM;
+ }
+
+ phy->dev = &pdev->dev;
+
+ phy->phy.dev = phy->dev;
+ phy->phy.label = "omap-usb2";
+ phy->phy.set_suspend = omap_usb2_suspend;
+ phy->phy.otg = otg;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+
+ phy->control_dev = devm_request_and_ioremap(&pdev->dev, res);
+ if (phy->control_dev == NULL) {
+ dev_err(&pdev->dev, "Failed to obtain io memory\n");
+ return -ENXIO;
+ }
+
+ phy->is_suspended = 1;
+ omap_usb_phy_power(phy, 0);
+
+ otg->set_host = omap_usb_set_host;
+ otg->set_peripheral = omap_usb_set_peripheral;
+ otg->set_vbus = omap_usb_set_vbus;
+ otg->start_srp = omap_usb_start_srp;
+ otg->phy = &phy->phy;
+
+ phy->wkupclk = devm_clk_get(phy->dev, "usb_phy_cm_clk32k");
+ if (IS_ERR(phy->wkupclk)) {
+ dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n");
+ return PTR_ERR(phy->wkupclk);
+ }
+ clk_prepare(phy->wkupclk);
+
+ usb_add_phy(&phy->phy, USB_PHY_TYPE_USB2);
+
+ platform_set_drvdata(pdev, phy);
+
+ pm_runtime_enable(phy->dev);
+
+ return 0;
+}
+
+static int __devexit omap_usb2_remove(struct platform_device *pdev)
+{
+ struct omap_usb *phy = platform_get_drvdata(pdev);
+
+ clk_unprepare(phy->wkupclk);
+ usb_remove_phy(&phy->phy);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_RUNTIME
+
+static int omap_usb2_runtime_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct omap_usb *phy = platform_get_drvdata(pdev);
+
+ clk_disable(phy->wkupclk);
+
+ return 0;
+}
+
+static int omap_usb2_runtime_resume(struct device *dev)
+{
+ u32 ret = 0;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct omap_usb *phy = platform_get_drvdata(pdev);
+
+ ret = clk_enable(phy->wkupclk);
+ if (ret < 0)
+ dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret);
+
+ return ret;
+}
+
+static const struct dev_pm_ops omap_usb2_pm_ops = {
+ SET_RUNTIME_PM_OPS(omap_usb2_runtime_suspend, omap_usb2_runtime_resume,
+ NULL)
+};
+
+#define DEV_PM_OPS (&omap_usb2_pm_ops)
+#else
+#define DEV_PM_OPS NULL
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id omap_usb2_id_table[] = {
+ { .compatible = "ti,omap-usb2" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, omap_usb2_id_table);
+#endif
+
+static struct platform_driver omap_usb2_driver = {
+ .probe = omap_usb2_probe,
+ .remove = __devexit_p(omap_usb2_remove),
+ .driver = {
+ .name = "omap-usb2",
+ .owner = THIS_MODULE,
+ .pm = DEV_PM_OPS,
+ .of_match_table = of_match_ptr(omap_usb2_id_table),
+ },
+};
+
+module_platform_driver(omap_usb2_driver);
+
+MODULE_ALIAS("platform: omap_usb2");
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("OMAP USB2 phy driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/phy/tegra_usb_phy.c b/drivers/usb/phy/tegra_usb_phy.c
new file mode 100644
index 000000000000..4739903245e8
--- /dev/null
+++ b/drivers/usb/phy/tegra_usb_phy.c
@@ -0,0 +1,839 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ * Erik Gilling <konkers@google.com>
+ * Benoit Goby <benoit@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/resource.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/ulpi.h>
+#include <asm/mach-types.h>
+#include <mach/gpio-tegra.h>
+#include <linux/usb/tegra_usb_phy.h>
+#include <mach/iomap.h>
+
+#define ULPI_VIEWPORT 0x170
+
+#define USB_PORTSC1 0x184
+#define USB_PORTSC1_PTS(x) (((x) & 0x3) << 30)
+#define USB_PORTSC1_PSPD(x) (((x) & 0x3) << 26)
+#define USB_PORTSC1_PHCD (1 << 23)
+#define USB_PORTSC1_WKOC (1 << 22)
+#define USB_PORTSC1_WKDS (1 << 21)
+#define USB_PORTSC1_WKCN (1 << 20)
+#define USB_PORTSC1_PTC(x) (((x) & 0xf) << 16)
+#define USB_PORTSC1_PP (1 << 12)
+#define USB_PORTSC1_SUSP (1 << 7)
+#define USB_PORTSC1_PE (1 << 2)
+#define USB_PORTSC1_CCS (1 << 0)
+
+#define USB_SUSP_CTRL 0x400
+#define USB_WAKE_ON_CNNT_EN_DEV (1 << 3)
+#define USB_WAKE_ON_DISCON_EN_DEV (1 << 4)
+#define USB_SUSP_CLR (1 << 5)
+#define USB_PHY_CLK_VALID (1 << 7)
+#define UTMIP_RESET (1 << 11)
+#define UHSIC_RESET (1 << 11)
+#define UTMIP_PHY_ENABLE (1 << 12)
+#define ULPI_PHY_ENABLE (1 << 13)
+#define USB_SUSP_SET (1 << 14)
+#define USB_WAKEUP_DEBOUNCE_COUNT(x) (((x) & 0x7) << 16)
+
+#define USB1_LEGACY_CTRL 0x410
+#define USB1_NO_LEGACY_MODE (1 << 0)
+#define USB1_VBUS_SENSE_CTL_MASK (3 << 1)
+#define USB1_VBUS_SENSE_CTL_VBUS_WAKEUP (0 << 1)
+#define USB1_VBUS_SENSE_CTL_AB_SESS_VLD_OR_VBUS_WAKEUP \
+ (1 << 1)
+#define USB1_VBUS_SENSE_CTL_AB_SESS_VLD (2 << 1)
+#define USB1_VBUS_SENSE_CTL_A_SESS_VLD (3 << 1)
+
+#define ULPI_TIMING_CTRL_0 0x424
+#define ULPI_OUTPUT_PINMUX_BYP (1 << 10)
+#define ULPI_CLKOUT_PINMUX_BYP (1 << 11)
+
+#define ULPI_TIMING_CTRL_1 0x428
+#define ULPI_DATA_TRIMMER_LOAD (1 << 0)
+#define ULPI_DATA_TRIMMER_SEL(x) (((x) & 0x7) << 1)
+#define ULPI_STPDIRNXT_TRIMMER_LOAD (1 << 16)
+#define ULPI_STPDIRNXT_TRIMMER_SEL(x) (((x) & 0x7) << 17)
+#define ULPI_DIR_TRIMMER_LOAD (1 << 24)
+#define ULPI_DIR_TRIMMER_SEL(x) (((x) & 0x7) << 25)
+
+#define UTMIP_PLL_CFG1 0x804
+#define UTMIP_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0)
+#define UTMIP_PLLU_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 27)
+
+#define UTMIP_XCVR_CFG0 0x808
+#define UTMIP_XCVR_SETUP(x) (((x) & 0xf) << 0)
+#define UTMIP_XCVR_LSRSLEW(x) (((x) & 0x3) << 8)
+#define UTMIP_XCVR_LSFSLEW(x) (((x) & 0x3) << 10)
+#define UTMIP_FORCE_PD_POWERDOWN (1 << 14)
+#define UTMIP_FORCE_PD2_POWERDOWN (1 << 16)
+#define UTMIP_FORCE_PDZI_POWERDOWN (1 << 18)
+#define UTMIP_XCVR_HSSLEW_MSB(x) (((x) & 0x7f) << 25)
+
+#define UTMIP_BIAS_CFG0 0x80c
+#define UTMIP_OTGPD (1 << 11)
+#define UTMIP_BIASPD (1 << 10)
+
+#define UTMIP_HSRX_CFG0 0x810
+#define UTMIP_ELASTIC_LIMIT(x) (((x) & 0x1f) << 10)
+#define UTMIP_IDLE_WAIT(x) (((x) & 0x1f) << 15)
+
+#define UTMIP_HSRX_CFG1 0x814
+#define UTMIP_HS_SYNC_START_DLY(x) (((x) & 0x1f) << 1)
+
+#define UTMIP_TX_CFG0 0x820
+#define UTMIP_FS_PREABMLE_J (1 << 19)
+#define UTMIP_HS_DISCON_DISABLE (1 << 8)
+
+#define UTMIP_MISC_CFG0 0x824
+#define UTMIP_DPDM_OBSERVE (1 << 26)
+#define UTMIP_DPDM_OBSERVE_SEL(x) (((x) & 0xf) << 27)
+#define UTMIP_DPDM_OBSERVE_SEL_FS_J UTMIP_DPDM_OBSERVE_SEL(0xf)
+#define UTMIP_DPDM_OBSERVE_SEL_FS_K UTMIP_DPDM_OBSERVE_SEL(0xe)
+#define UTMIP_DPDM_OBSERVE_SEL_FS_SE1 UTMIP_DPDM_OBSERVE_SEL(0xd)
+#define UTMIP_DPDM_OBSERVE_SEL_FS_SE0 UTMIP_DPDM_OBSERVE_SEL(0xc)
+#define UTMIP_SUSPEND_EXIT_ON_EDGE (1 << 22)
+
+#define UTMIP_MISC_CFG1 0x828
+#define UTMIP_PLL_ACTIVE_DLY_COUNT(x) (((x) & 0x1f) << 18)
+#define UTMIP_PLLU_STABLE_COUNT(x) (((x) & 0xfff) << 6)
+
+#define UTMIP_DEBOUNCE_CFG0 0x82c
+#define UTMIP_BIAS_DEBOUNCE_A(x) (((x) & 0xffff) << 0)
+
+#define UTMIP_BAT_CHRG_CFG0 0x830
+#define UTMIP_PD_CHRG (1 << 0)
+
+#define UTMIP_SPARE_CFG0 0x834
+#define FUSE_SETUP_SEL (1 << 3)
+
+#define UTMIP_XCVR_CFG1 0x838
+#define UTMIP_FORCE_PDDISC_POWERDOWN (1 << 0)
+#define UTMIP_FORCE_PDCHRP_POWERDOWN (1 << 2)
+#define UTMIP_FORCE_PDDR_POWERDOWN (1 << 4)
+#define UTMIP_XCVR_TERM_RANGE_ADJ(x) (((x) & 0xf) << 18)
+
+#define UTMIP_BIAS_CFG1 0x83c
+#define UTMIP_BIAS_PDTRK_COUNT(x) (((x) & 0x1f) << 3)
+
+static DEFINE_SPINLOCK(utmip_pad_lock);
+static int utmip_pad_count;
+
+struct tegra_xtal_freq {
+ int freq;
+ u8 enable_delay;
+ u8 stable_count;
+ u8 active_delay;
+ u8 xtal_freq_count;
+ u16 debounce;
+};
+
+static const struct tegra_xtal_freq tegra_freq_table[] = {
+ {
+ .freq = 12000000,
+ .enable_delay = 0x02,
+ .stable_count = 0x2F,
+ .active_delay = 0x04,
+ .xtal_freq_count = 0x76,
+ .debounce = 0x7530,
+ },
+ {
+ .freq = 13000000,
+ .enable_delay = 0x02,
+ .stable_count = 0x33,
+ .active_delay = 0x05,
+ .xtal_freq_count = 0x7F,
+ .debounce = 0x7EF4,
+ },
+ {
+ .freq = 19200000,
+ .enable_delay = 0x03,
+ .stable_count = 0x4B,
+ .active_delay = 0x06,
+ .xtal_freq_count = 0xBB,
+ .debounce = 0xBB80,
+ },
+ {
+ .freq = 26000000,
+ .enable_delay = 0x04,
+ .stable_count = 0x66,
+ .active_delay = 0x09,
+ .xtal_freq_count = 0xFE,
+ .debounce = 0xFDE8,
+ },
+};
+
+static struct tegra_utmip_config utmip_default[] = {
+ [0] = {
+ .hssync_start_delay = 9,
+ .idle_wait_delay = 17,
+ .elastic_limit = 16,
+ .term_range_adj = 6,
+ .xcvr_setup = 9,
+ .xcvr_lsfslew = 1,
+ .xcvr_lsrslew = 1,
+ },
+ [2] = {
+ .hssync_start_delay = 9,
+ .idle_wait_delay = 17,
+ .elastic_limit = 16,
+ .term_range_adj = 6,
+ .xcvr_setup = 9,
+ .xcvr_lsfslew = 2,
+ .xcvr_lsrslew = 2,
+ },
+};
+
+static inline bool phy_is_ulpi(struct tegra_usb_phy *phy)
+{
+ return (phy->instance == 1);
+}
+
+static int utmip_pad_open(struct tegra_usb_phy *phy)
+{
+ phy->pad_clk = clk_get_sys("utmip-pad", NULL);
+ if (IS_ERR(phy->pad_clk)) {
+ pr_err("%s: can't get utmip pad clock\n", __func__);
+ return PTR_ERR(phy->pad_clk);
+ }
+
+ if (phy->instance == 0) {
+ phy->pad_regs = phy->regs;
+ } else {
+ phy->pad_regs = ioremap(TEGRA_USB_BASE, TEGRA_USB_SIZE);
+ if (!phy->pad_regs) {
+ pr_err("%s: can't remap usb registers\n", __func__);
+ clk_put(phy->pad_clk);
+ return -ENOMEM;
+ }
+ }
+ return 0;
+}
+
+static void utmip_pad_close(struct tegra_usb_phy *phy)
+{
+ if (phy->instance != 0)
+ iounmap(phy->pad_regs);
+ clk_put(phy->pad_clk);
+}
+
+static void utmip_pad_power_on(struct tegra_usb_phy *phy)
+{
+ unsigned long val, flags;
+ void __iomem *base = phy->pad_regs;
+
+ clk_prepare_enable(phy->pad_clk);
+
+ spin_lock_irqsave(&utmip_pad_lock, flags);
+
+ if (utmip_pad_count++ == 0) {
+ val = readl(base + UTMIP_BIAS_CFG0);
+ val &= ~(UTMIP_OTGPD | UTMIP_BIASPD);
+ writel(val, base + UTMIP_BIAS_CFG0);
+ }
+
+ spin_unlock_irqrestore(&utmip_pad_lock, flags);
+
+ clk_disable_unprepare(phy->pad_clk);
+}
+
+static int utmip_pad_power_off(struct tegra_usb_phy *phy)
+{
+ unsigned long val, flags;
+ void __iomem *base = phy->pad_regs;
+
+ if (!utmip_pad_count) {
+ pr_err("%s: utmip pad already powered off\n", __func__);
+ return -EINVAL;
+ }
+
+ clk_prepare_enable(phy->pad_clk);
+
+ spin_lock_irqsave(&utmip_pad_lock, flags);
+
+ if (--utmip_pad_count == 0) {
+ val = readl(base + UTMIP_BIAS_CFG0);
+ val |= UTMIP_OTGPD | UTMIP_BIASPD;
+ writel(val, base + UTMIP_BIAS_CFG0);
+ }
+
+ spin_unlock_irqrestore(&utmip_pad_lock, flags);
+
+ clk_disable_unprepare(phy->pad_clk);
+
+ return 0;
+}
+
+static int utmi_wait_register(void __iomem *reg, u32 mask, u32 result)
+{
+ unsigned long timeout = 2000;
+ do {
+ if ((readl(reg) & mask) == result)
+ return 0;
+ udelay(1);
+ timeout--;
+ } while (timeout);
+ return -1;
+}
+
+static void utmi_phy_clk_disable(struct tegra_usb_phy *phy)
+{
+ unsigned long val;
+ void __iomem *base = phy->regs;
+
+ if (phy->instance == 0) {
+ val = readl(base + USB_SUSP_CTRL);
+ val |= USB_SUSP_SET;
+ writel(val, base + USB_SUSP_CTRL);
+
+ udelay(10);
+
+ val = readl(base + USB_SUSP_CTRL);
+ val &= ~USB_SUSP_SET;
+ writel(val, base + USB_SUSP_CTRL);
+ }
+
+ if (phy->instance == 2) {
+ val = readl(base + USB_PORTSC1);
+ val |= USB_PORTSC1_PHCD;
+ writel(val, base + USB_PORTSC1);
+ }
+
+ if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, 0) < 0)
+ pr_err("%s: timeout waiting for phy to stabilize\n", __func__);
+}
+
+static void utmi_phy_clk_enable(struct tegra_usb_phy *phy)
+{
+ unsigned long val;
+ void __iomem *base = phy->regs;
+
+ if (phy->instance == 0) {
+ val = readl(base + USB_SUSP_CTRL);
+ val |= USB_SUSP_CLR;
+ writel(val, base + USB_SUSP_CTRL);
+
+ udelay(10);
+
+ val = readl(base + USB_SUSP_CTRL);
+ val &= ~USB_SUSP_CLR;
+ writel(val, base + USB_SUSP_CTRL);
+ }
+
+ if (phy->instance == 2) {
+ val = readl(base + USB_PORTSC1);
+ val &= ~USB_PORTSC1_PHCD;
+ writel(val, base + USB_PORTSC1);
+ }
+
+ if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID,
+ USB_PHY_CLK_VALID))
+ pr_err("%s: timeout waiting for phy to stabilize\n", __func__);
+}
+
+static int utmi_phy_power_on(struct tegra_usb_phy *phy)
+{
+ unsigned long val;
+ void __iomem *base = phy->regs;
+ struct tegra_utmip_config *config = phy->config;
+
+ val = readl(base + USB_SUSP_CTRL);
+ val |= UTMIP_RESET;
+ writel(val, base + USB_SUSP_CTRL);
+
+ if (phy->instance == 0) {
+ val = readl(base + USB1_LEGACY_CTRL);
+ val |= USB1_NO_LEGACY_MODE;
+ writel(val, base + USB1_LEGACY_CTRL);
+ }
+
+ val = readl(base + UTMIP_TX_CFG0);
+ val &= ~UTMIP_FS_PREABMLE_J;
+ writel(val, base + UTMIP_TX_CFG0);
+
+ val = readl(base + UTMIP_HSRX_CFG0);
+ val &= ~(UTMIP_IDLE_WAIT(~0) | UTMIP_ELASTIC_LIMIT(~0));
+ val |= UTMIP_IDLE_WAIT(config->idle_wait_delay);
+ val |= UTMIP_ELASTIC_LIMIT(config->elastic_limit);
+ writel(val, base + UTMIP_HSRX_CFG0);
+
+ val = readl(base + UTMIP_HSRX_CFG1);
+ val &= ~UTMIP_HS_SYNC_START_DLY(~0);
+ val |= UTMIP_HS_SYNC_START_DLY(config->hssync_start_delay);
+ writel(val, base + UTMIP_HSRX_CFG1);
+
+ val = readl(base + UTMIP_DEBOUNCE_CFG0);
+ val &= ~UTMIP_BIAS_DEBOUNCE_A(~0);
+ val |= UTMIP_BIAS_DEBOUNCE_A(phy->freq->debounce);
+ writel(val, base + UTMIP_DEBOUNCE_CFG0);
+
+ val = readl(base + UTMIP_MISC_CFG0);
+ val &= ~UTMIP_SUSPEND_EXIT_ON_EDGE;
+ writel(val, base + UTMIP_MISC_CFG0);
+
+ val = readl(base + UTMIP_MISC_CFG1);
+ val &= ~(UTMIP_PLL_ACTIVE_DLY_COUNT(~0) | UTMIP_PLLU_STABLE_COUNT(~0));
+ val |= UTMIP_PLL_ACTIVE_DLY_COUNT(phy->freq->active_delay) |
+ UTMIP_PLLU_STABLE_COUNT(phy->freq->stable_count);
+ writel(val, base + UTMIP_MISC_CFG1);
+
+ val = readl(base + UTMIP_PLL_CFG1);
+ val &= ~(UTMIP_XTAL_FREQ_COUNT(~0) | UTMIP_PLLU_ENABLE_DLY_COUNT(~0));
+ val |= UTMIP_XTAL_FREQ_COUNT(phy->freq->xtal_freq_count) |
+ UTMIP_PLLU_ENABLE_DLY_COUNT(phy->freq->enable_delay);
+ writel(val, base + UTMIP_PLL_CFG1);
+
+ if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE) {
+ val = readl(base + USB_SUSP_CTRL);
+ val &= ~(USB_WAKE_ON_CNNT_EN_DEV | USB_WAKE_ON_DISCON_EN_DEV);
+ writel(val, base + USB_SUSP_CTRL);
+ }
+
+ utmip_pad_power_on(phy);
+
+ val = readl(base + UTMIP_XCVR_CFG0);
+ val &= ~(UTMIP_FORCE_PD_POWERDOWN | UTMIP_FORCE_PD2_POWERDOWN |
+ UTMIP_FORCE_PDZI_POWERDOWN | UTMIP_XCVR_SETUP(~0) |
+ UTMIP_XCVR_LSFSLEW(~0) | UTMIP_XCVR_LSRSLEW(~0) |
+ UTMIP_XCVR_HSSLEW_MSB(~0));
+ val |= UTMIP_XCVR_SETUP(config->xcvr_setup);
+ val |= UTMIP_XCVR_LSFSLEW(config->xcvr_lsfslew);
+ val |= UTMIP_XCVR_LSRSLEW(config->xcvr_lsrslew);
+ writel(val, base + UTMIP_XCVR_CFG0);
+
+ val = readl(base + UTMIP_XCVR_CFG1);
+ val &= ~(UTMIP_FORCE_PDDISC_POWERDOWN | UTMIP_FORCE_PDCHRP_POWERDOWN |
+ UTMIP_FORCE_PDDR_POWERDOWN | UTMIP_XCVR_TERM_RANGE_ADJ(~0));
+ val |= UTMIP_XCVR_TERM_RANGE_ADJ(config->term_range_adj);
+ writel(val, base + UTMIP_XCVR_CFG1);
+
+ val = readl(base + UTMIP_BAT_CHRG_CFG0);
+ val &= ~UTMIP_PD_CHRG;
+ writel(val, base + UTMIP_BAT_CHRG_CFG0);
+
+ val = readl(base + UTMIP_BIAS_CFG1);
+ val &= ~UTMIP_BIAS_PDTRK_COUNT(~0);
+ val |= UTMIP_BIAS_PDTRK_COUNT(0x5);
+ writel(val, base + UTMIP_BIAS_CFG1);
+
+ if (phy->instance == 0) {
+ val = readl(base + UTMIP_SPARE_CFG0);
+ if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE)
+ val &= ~FUSE_SETUP_SEL;
+ else
+ val |= FUSE_SETUP_SEL;
+ writel(val, base + UTMIP_SPARE_CFG0);
+ }
+
+ if (phy->instance == 2) {
+ val = readl(base + USB_SUSP_CTRL);
+ val |= UTMIP_PHY_ENABLE;
+ writel(val, base + USB_SUSP_CTRL);
+ }
+
+ val = readl(base + USB_SUSP_CTRL);
+ val &= ~UTMIP_RESET;
+ writel(val, base + USB_SUSP_CTRL);
+
+ if (phy->instance == 0) {
+ val = readl(base + USB1_LEGACY_CTRL);
+ val &= ~USB1_VBUS_SENSE_CTL_MASK;
+ val |= USB1_VBUS_SENSE_CTL_A_SESS_VLD;
+ writel(val, base + USB1_LEGACY_CTRL);
+
+ val = readl(base + USB_SUSP_CTRL);
+ val &= ~USB_SUSP_SET;
+ writel(val, base + USB_SUSP_CTRL);
+ }
+
+ utmi_phy_clk_enable(phy);
+
+ if (phy->instance == 2) {
+ val = readl(base + USB_PORTSC1);
+ val &= ~USB_PORTSC1_PTS(~0);
+ writel(val, base + USB_PORTSC1);
+ }
+
+ return 0;
+}
+
+static int utmi_phy_power_off(struct tegra_usb_phy *phy)
+{
+ unsigned long val;
+ void __iomem *base = phy->regs;
+
+ utmi_phy_clk_disable(phy);
+
+ if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE) {
+ val = readl(base + USB_SUSP_CTRL);
+ val &= ~USB_WAKEUP_DEBOUNCE_COUNT(~0);
+ val |= USB_WAKE_ON_CNNT_EN_DEV | USB_WAKEUP_DEBOUNCE_COUNT(5);
+ writel(val, base + USB_SUSP_CTRL);
+ }
+
+ val = readl(base + USB_SUSP_CTRL);
+ val |= UTMIP_RESET;
+ writel(val, base + USB_SUSP_CTRL);
+
+ val = readl(base + UTMIP_BAT_CHRG_CFG0);
+ val |= UTMIP_PD_CHRG;
+ writel(val, base + UTMIP_BAT_CHRG_CFG0);
+
+ val = readl(base + UTMIP_XCVR_CFG0);
+ val |= UTMIP_FORCE_PD_POWERDOWN | UTMIP_FORCE_PD2_POWERDOWN |
+ UTMIP_FORCE_PDZI_POWERDOWN;
+ writel(val, base + UTMIP_XCVR_CFG0);
+
+ val = readl(base + UTMIP_XCVR_CFG1);
+ val |= UTMIP_FORCE_PDDISC_POWERDOWN | UTMIP_FORCE_PDCHRP_POWERDOWN |
+ UTMIP_FORCE_PDDR_POWERDOWN;
+ writel(val, base + UTMIP_XCVR_CFG1);
+
+ return utmip_pad_power_off(phy);
+}
+
+static void utmi_phy_preresume(struct tegra_usb_phy *phy)
+{
+ unsigned long val;
+ void __iomem *base = phy->regs;
+
+ val = readl(base + UTMIP_TX_CFG0);
+ val |= UTMIP_HS_DISCON_DISABLE;
+ writel(val, base + UTMIP_TX_CFG0);
+}
+
+static void utmi_phy_postresume(struct tegra_usb_phy *phy)
+{
+ unsigned long val;
+ void __iomem *base = phy->regs;
+
+ val = readl(base + UTMIP_TX_CFG0);
+ val &= ~UTMIP_HS_DISCON_DISABLE;
+ writel(val, base + UTMIP_TX_CFG0);
+}
+
+static void utmi_phy_restore_start(struct tegra_usb_phy *phy,
+ enum tegra_usb_phy_port_speed port_speed)
+{
+ unsigned long val;
+ void __iomem *base = phy->regs;
+
+ val = readl(base + UTMIP_MISC_CFG0);
+ val &= ~UTMIP_DPDM_OBSERVE_SEL(~0);
+ if (port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW)
+ val |= UTMIP_DPDM_OBSERVE_SEL_FS_K;
+ else
+ val |= UTMIP_DPDM_OBSERVE_SEL_FS_J;
+ writel(val, base + UTMIP_MISC_CFG0);
+ udelay(1);
+
+ val = readl(base + UTMIP_MISC_CFG0);
+ val |= UTMIP_DPDM_OBSERVE;
+ writel(val, base + UTMIP_MISC_CFG0);
+ udelay(10);
+}
+
+static void utmi_phy_restore_end(struct tegra_usb_phy *phy)
+{
+ unsigned long val;
+ void __iomem *base = phy->regs;
+
+ val = readl(base + UTMIP_MISC_CFG0);
+ val &= ~UTMIP_DPDM_OBSERVE;
+ writel(val, base + UTMIP_MISC_CFG0);
+ udelay(10);
+}
+
+static int ulpi_phy_power_on(struct tegra_usb_phy *phy)
+{
+ int ret;
+ unsigned long val;
+ void __iomem *base = phy->regs;
+ struct tegra_ulpi_config *config = phy->config;
+
+ gpio_direction_output(config->reset_gpio, 0);
+ msleep(5);
+ gpio_direction_output(config->reset_gpio, 1);
+
+ clk_prepare_enable(phy->clk);
+ msleep(1);
+
+ val = readl(base + USB_SUSP_CTRL);
+ val |= UHSIC_RESET;
+ writel(val, base + USB_SUSP_CTRL);
+
+ val = readl(base + ULPI_TIMING_CTRL_0);
+ val |= ULPI_OUTPUT_PINMUX_BYP | ULPI_CLKOUT_PINMUX_BYP;
+ writel(val, base + ULPI_TIMING_CTRL_0);
+
+ val = readl(base + USB_SUSP_CTRL);
+ val |= ULPI_PHY_ENABLE;
+ writel(val, base + USB_SUSP_CTRL);
+
+ val = 0;
+ writel(val, base + ULPI_TIMING_CTRL_1);
+
+ val |= ULPI_DATA_TRIMMER_SEL(4);
+ val |= ULPI_STPDIRNXT_TRIMMER_SEL(4);
+ val |= ULPI_DIR_TRIMMER_SEL(4);
+ writel(val, base + ULPI_TIMING_CTRL_1);
+ udelay(10);
+
+ val |= ULPI_DATA_TRIMMER_LOAD;
+ val |= ULPI_STPDIRNXT_TRIMMER_LOAD;
+ val |= ULPI_DIR_TRIMMER_LOAD;
+ writel(val, base + ULPI_TIMING_CTRL_1);
+
+ /* Fix VbusInvalid due to floating VBUS */
+ ret = usb_phy_io_write(phy->ulpi, 0x40, 0x08);
+ if (ret) {
+ pr_err("%s: ulpi write failed\n", __func__);
+ return ret;
+ }
+
+ ret = usb_phy_io_write(phy->ulpi, 0x80, 0x0B);
+ if (ret) {
+ pr_err("%s: ulpi write failed\n", __func__);
+ return ret;
+ }
+
+ val = readl(base + USB_PORTSC1);
+ val |= USB_PORTSC1_WKOC | USB_PORTSC1_WKDS | USB_PORTSC1_WKCN;
+ writel(val, base + USB_PORTSC1);
+
+ val = readl(base + USB_SUSP_CTRL);
+ val |= USB_SUSP_CLR;
+ writel(val, base + USB_SUSP_CTRL);
+ udelay(100);
+
+ val = readl(base + USB_SUSP_CTRL);
+ val &= ~USB_SUSP_CLR;
+ writel(val, base + USB_SUSP_CTRL);
+
+ return 0;
+}
+
+static int ulpi_phy_power_off(struct tegra_usb_phy *phy)
+{
+ unsigned long val;
+ void __iomem *base = phy->regs;
+ struct tegra_ulpi_config *config = phy->config;
+
+ /* Clear WKCN/WKDS/WKOC wake-on events that can cause the USB
+ * Controller to immediately bring the ULPI PHY out of low power
+ */
+ val = readl(base + USB_PORTSC1);
+ val &= ~(USB_PORTSC1_WKOC | USB_PORTSC1_WKDS | USB_PORTSC1_WKCN);
+ writel(val, base + USB_PORTSC1);
+
+ clk_disable(phy->clk);
+ return gpio_direction_output(config->reset_gpio, 0);
+}
+
+static int tegra_phy_init(struct usb_phy *x)
+{
+ struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy);
+ struct tegra_ulpi_config *ulpi_config;
+ int err;
+
+ if (phy_is_ulpi(phy)) {
+ ulpi_config = phy->config;
+ phy->clk = clk_get_sys(NULL, ulpi_config->clk);
+ if (IS_ERR(phy->clk)) {
+ pr_err("%s: can't get ulpi clock\n", __func__);
+ err = -ENXIO;
+ goto err1;
+ }
+ if (!gpio_is_valid(ulpi_config->reset_gpio))
+ ulpi_config->reset_gpio =
+ of_get_named_gpio(phy->dev->of_node,
+ "nvidia,phy-reset-gpio", 0);
+ if (!gpio_is_valid(ulpi_config->reset_gpio)) {
+ pr_err("%s: invalid reset gpio: %d\n", __func__,
+ ulpi_config->reset_gpio);
+ err = -EINVAL;
+ goto err1;
+ }
+ gpio_request(ulpi_config->reset_gpio, "ulpi_phy_reset_b");
+ gpio_direction_output(ulpi_config->reset_gpio, 0);
+ phy->ulpi = otg_ulpi_create(&ulpi_viewport_access_ops, 0);
+ phy->ulpi->io_priv = phy->regs + ULPI_VIEWPORT;
+ } else {
+ err = utmip_pad_open(phy);
+ if (err < 0)
+ goto err1;
+ }
+ return 0;
+err1:
+ clk_disable_unprepare(phy->pll_u);
+ clk_put(phy->pll_u);
+ return err;
+}
+
+static void tegra_usb_phy_close(struct usb_phy *x)
+{
+ struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy);
+
+ if (phy_is_ulpi(phy))
+ clk_put(phy->clk);
+ else
+ utmip_pad_close(phy);
+ clk_disable_unprepare(phy->pll_u);
+ clk_put(phy->pll_u);
+ kfree(phy);
+}
+
+static int tegra_usb_phy_power_on(struct tegra_usb_phy *phy)
+{
+ if (phy_is_ulpi(phy))
+ return ulpi_phy_power_on(phy);
+ else
+ return utmi_phy_power_on(phy);
+}
+
+static int tegra_usb_phy_power_off(struct tegra_usb_phy *phy)
+{
+ if (phy_is_ulpi(phy))
+ return ulpi_phy_power_off(phy);
+ else
+ return utmi_phy_power_off(phy);
+}
+
+static int tegra_usb_phy_suspend(struct usb_phy *x, int suspend)
+{
+ struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy);
+ if (suspend)
+ return tegra_usb_phy_power_off(phy);
+ else
+ return tegra_usb_phy_power_on(phy);
+}
+
+struct tegra_usb_phy *tegra_usb_phy_open(struct device *dev, int instance,
+ void __iomem *regs, void *config, enum tegra_usb_phy_mode phy_mode)
+{
+ struct tegra_usb_phy *phy;
+ unsigned long parent_rate;
+ int i;
+ int err;
+
+ phy = kmalloc(sizeof(struct tegra_usb_phy), GFP_KERNEL);
+ if (!phy)
+ return ERR_PTR(-ENOMEM);
+
+ phy->instance = instance;
+ phy->regs = regs;
+ phy->config = config;
+ phy->mode = phy_mode;
+ phy->dev = dev;
+
+ if (!phy->config) {
+ if (phy_is_ulpi(phy)) {
+ pr_err("%s: ulpi phy configuration missing", __func__);
+ err = -EINVAL;
+ goto err0;
+ } else {
+ phy->config = &utmip_default[instance];
+ }
+ }
+
+ phy->pll_u = clk_get_sys(NULL, "pll_u");
+ if (IS_ERR(phy->pll_u)) {
+ pr_err("Can't get pll_u clock\n");
+ err = PTR_ERR(phy->pll_u);
+ goto err0;
+ }
+ clk_prepare_enable(phy->pll_u);
+
+ parent_rate = clk_get_rate(clk_get_parent(phy->pll_u));
+ for (i = 0; i < ARRAY_SIZE(tegra_freq_table); i++) {
+ if (tegra_freq_table[i].freq == parent_rate) {
+ phy->freq = &tegra_freq_table[i];
+ break;
+ }
+ }
+ if (!phy->freq) {
+ pr_err("invalid pll_u parent rate %ld\n", parent_rate);
+ err = -EINVAL;
+ goto err1;
+ }
+
+ phy->u_phy.init = tegra_phy_init;
+ phy->u_phy.shutdown = tegra_usb_phy_close;
+ phy->u_phy.set_suspend = tegra_usb_phy_suspend;
+
+ return phy;
+
+err1:
+ clk_disable_unprepare(phy->pll_u);
+ clk_put(phy->pll_u);
+err0:
+ kfree(phy);
+ return ERR_PTR(err);
+}
+EXPORT_SYMBOL_GPL(tegra_usb_phy_open);
+
+void tegra_usb_phy_preresume(struct tegra_usb_phy *phy)
+{
+ if (!phy_is_ulpi(phy))
+ utmi_phy_preresume(phy);
+}
+EXPORT_SYMBOL_GPL(tegra_usb_phy_preresume);
+
+void tegra_usb_phy_postresume(struct tegra_usb_phy *phy)
+{
+ if (!phy_is_ulpi(phy))
+ utmi_phy_postresume(phy);
+}
+EXPORT_SYMBOL_GPL(tegra_usb_phy_postresume);
+
+void tegra_ehci_phy_restore_start(struct tegra_usb_phy *phy,
+ enum tegra_usb_phy_port_speed port_speed)
+{
+ if (!phy_is_ulpi(phy))
+ utmi_phy_restore_start(phy, port_speed);
+}
+EXPORT_SYMBOL_GPL(tegra_ehci_phy_restore_start);
+
+void tegra_ehci_phy_restore_end(struct tegra_usb_phy *phy)
+{
+ if (!phy_is_ulpi(phy))
+ utmi_phy_restore_end(phy);
+}
+EXPORT_SYMBOL_GPL(tegra_ehci_phy_restore_end);
+
+void tegra_usb_phy_clk_disable(struct tegra_usb_phy *phy)
+{
+ if (!phy_is_ulpi(phy))
+ utmi_phy_clk_disable(phy);
+}
+EXPORT_SYMBOL_GPL(tegra_usb_phy_clk_disable);
+
+void tegra_usb_phy_clk_enable(struct tegra_usb_phy *phy)
+{
+ if (!phy_is_ulpi(phy))
+ utmi_phy_clk_enable(phy);
+}
+EXPORT_SYMBOL_GPL(tegra_usb_phy_clk_enable);