[ARM] tegra: Setup USB PHY as recommended by NVIDIA

This fixes enumeration issues with some devices

Change-Id: I6283a6fb49a9e4505ad388cacdd88ecf1bdf1b9d
Signed-off-by: Benoit Goby <benoit@android.com>
This commit is contained in:
Benoit Goby
2010-07-23 17:02:26 -07:00
committed by Colin Cross
parent 288b58e155
commit a0e0319691
4 changed files with 56 additions and 26 deletions

View 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);

View 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;
}

View 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);
}

View 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) {