[ARM] tegra: Setup USB PHY as recommended by NVIDIA
authorBenoit Goby <benoit@android.com>
Sat, 24 Jul 2010 00:02:26 +0000 (17:02 -0700)
committerColin Cross <ccross@android.com>
Wed, 6 Oct 2010 23:27:17 +0000 (16:27 -0700)
This fixes enumeration issues with some devices

Change-Id: I6283a6fb49a9e4505ad388cacdd88ecf1bdf1b9d
Signed-off-by: Benoit Goby <benoit@android.com>
arch/arm/mach-tegra/include/mach/usb_phy.h
arch/arm/mach-tegra/usb_phy.c
drivers/usb/gadget/fsl_tegra_udc.c
drivers/usb/host/ehci-tegra.c

index 78179ef58fde83b1527c5ca7edc743d0910f0c79..285c65cc511c1a6f6c6ba7f6557dc3981e3e81d2 100644 (file)
@@ -26,9 +26,15 @@ struct tegra_usb_phy {
        struct clk *pll_u;
 };
 
+enum tegra_usb_phy_mode {
+       TEGRA_USB_PHY_MODE_DEVICE,
+       TEGRA_USB_PHY_MODE_HOST,
+};
+
 struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs);
 
-int tegra_usb_phy_power_on(struct tegra_usb_phy *phy);
+int tegra_usb_phy_power_on(struct tegra_usb_phy *phy,
+                          enum tegra_usb_phy_mode phy_mode);
 
 int tegra_usb_phy_power_off(struct tegra_usb_phy *phy);
 
index 602f4afa1eeff8c39f204f3da82914ae4e7e8c5b..fb829861f5d47b07f9e776b6790b938756d1497d 100644 (file)
@@ -35,6 +35,7 @@
 #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   UTMIP_PHY_ENABLE             (1 << 12)
 #define   USB_SUSP_SET         (1 << 14)
@@ -54,6 +55,8 @@
 
 #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)
@@ -117,10 +120,27 @@ static const u16 udc_debounce_table[] = {
        0xFDE8, /* 26 MHz */
 };
 
+static int utmi_phy_wait_stable(struct tegra_usb_phy *phy)
+{
+       void __iomem *base = phy->regs;
+       unsigned long timeout = jiffies + HZ;
+
+       while (time_before(jiffies, timeout)) {
+               if (readl(base + USB_SUSP_CTRL) & USB_PHY_CLK_VALID)
+                       return 0;
+               udelay(10);
+               cpu_relax();
+       }
+       if (readl(base + USB_SUSP_CTRL) & USB_PHY_CLK_VALID)
+               return 0;
+       else
+               return -ETIMEDOUT;
+}
+
 static void utmi_phy_init(struct tegra_usb_phy *phy, int freq_sel)
 {
        unsigned long val;
-       void *base = phy->regs;
+       void __iomem *base = phy->regs;
 
        val = readl(base + USB_SUSP_CTRL);
        val |= UTMIP_RESET;
@@ -132,10 +152,6 @@ static void utmi_phy_init(struct tegra_usb_phy *phy, int freq_sel)
                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(17) | UTMIP_ELASTIC_LIMIT(16);
@@ -168,27 +184,31 @@ static void utmi_phy_init(struct tegra_usb_phy *phy, int freq_sel)
        writel(val, base + UTMIP_PLL_CFG1);
 }
 
-void utmi_phy_power_on(struct tegra_usb_phy *phy)
+void utmi_phy_power_on(struct tegra_usb_phy *phy,
+                      enum tegra_usb_phy_mode phy_mode)
 {
        unsigned long val;
-       void *base = phy->regs;
+       void __iomem *base = phy->regs;
 
-       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);
+       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);
+       }
 
        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_HSSLEW_MSB(~0));
-       val |= UTMIP_XCVR_SETUP(0xF);
-       /* TODO: slow rise/fall times in host mode */
+                UTMIP_FORCE_PDZI_POWERDOWN | UTMIP_XCVR_SETUP(~0));
+       if (phy_mode == TEGRA_USB_PHY_MODE_HOST) {
+               val &= ~(UTMIP_XCVR_LSFSLEW(~0) | UTMIP_XCVR_LSRSLEW(~0));
+               val |= UTMIP_XCVR_LSFSLEW(2) | UTMIP_XCVR_LSRSLEW(2);
+               val |= UTMIP_XCVR_SETUP(0x9);
+       } else {
+               val &= ~(UTMIP_XCVR_HSSLEW_MSB(~0));
+               val |= UTMIP_XCVR_SETUP(0xF);
+       }
        writel(val, base + UTMIP_XCVR_CFG0);
 
-       val = readl(base + UTMIP_SPARE_CFG0);
-       val &= ~FUSE_SETUP_SEL;
-       writel(val, base + UTMIP_SPARE_CFG0);
-
        val = readl(base + UTMIP_BIAS_CFG0);
        val &= ~(UTMIP_OTGPD | UTMIP_BIASPD);
        writel(val, base + UTMIP_BIAS_CFG0);
@@ -234,6 +254,9 @@ void utmi_phy_power_on(struct tegra_usb_phy *phy)
                writel(val, base + USB_SUSP_CTRL);
        }
 
+       if (utmi_phy_wait_stable(phy))
+               pr_err("%s: timeout waiting for phy to stabilize\n", __func__);
+
        if (phy->instance == 2) {
                val = readl(base + USB_PORTSC1);
                val &= ~USB_PORTSC1_PTS(~0);
@@ -244,7 +267,7 @@ void utmi_phy_power_on(struct tegra_usb_phy *phy)
 void utmi_phy_power_off(struct tegra_usb_phy *phy)
 {
        unsigned long val;
-       void *base = phy->regs;
+       void __iomem *base = phy->regs;
 
        if (phy->instance == 0) {
                val = readl(base + USB_SUSP_CTRL);
@@ -333,12 +356,13 @@ err0:
        return ERR_PTR(err);
 }
 
-int tegra_usb_phy_power_on(struct tegra_usb_phy *phy)
+int tegra_usb_phy_power_on(struct tegra_usb_phy *phy,
+                          enum tegra_usb_phy_mode phy_mode)
 {
        /* TODO usb2 ulpi */
        clk_enable(phy->pll_u);
        if (phy->instance != 1)
-               utmi_phy_power_on(phy);
+               utmi_phy_power_on(phy, phy_mode);
 
        return 0;
 }
index bdd80df09d856de1878230f2f5a1f4fac8a07737..fc666bbd6b8b8b866e88c2d55688596abdec4f55 100644 (file)
@@ -56,7 +56,7 @@ int fsl_udc_clk_init(struct platform_device *pdev)
                goto err1;
        }
 
-       tegra_usb_phy_power_on(phy);
+       tegra_usb_phy_power_on(phy, TEGRA_USB_PHY_MODE_DEVICE);
 
        return 0;
 err1:
@@ -90,5 +90,5 @@ void fsl_udc_clk_suspend(void)
 void fsl_udc_clk_resume(void)
 {
        clk_enable(udc_clk);
-       tegra_usb_phy_power_on(phy);
+       tegra_usb_phy_power_on(phy, TEGRA_USB_PHY_MODE_DEVICE);
 }
index 40eff7f0d323912eec5ceb4694aec68250564081..b93aefb6f3c3f997c36dd7629ea30b2f4cd6f260 100644 (file)
@@ -50,7 +50,7 @@ static void tegra_ehci_power_up(struct usb_hcd *hcd)
        struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller);
 
        clk_enable(tegra->clk);
-       tegra_usb_phy_power_on(tegra->phy);
+       tegra_usb_phy_power_on(tegra->phy, TEGRA_USB_PHY_MODE_HOST);
        tegra->host_resumed = 1;
 }
 
@@ -419,7 +419,7 @@ static int tegra_ehci_probe(struct platform_device *pdev)
                goto fail_phy;
        }
 
-       tegra_usb_phy_power_on(tegra->phy);
+       tegra_usb_phy_power_on(tegra->phy, TEGRA_USB_PHY_MODE_HOST);
 
        err = tegra_ehci_reset(hcd);
        if (err) {