From b3661dcd2ea92ee1f81e64565e44d5afb32c9923 Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Thu, 2 Sep 2010 17:47:06 -0700 Subject: [PATCH] [ARM] tegra: usb_phy: Add support for usb2 ulpi external phy Change-Id: Ie2ed0d22abae1319996fe0a6caf28ec7d7e4313d Signed-off-by: Benoit Goby --- arch/arm/mach-tegra/include/mach/usb_phy.h | 11 +- arch/arm/mach-tegra/usb_phy.c | 183 +++++++++++++++++++-- 2 files changed, 180 insertions(+), 14 deletions(-) diff --git a/arch/arm/mach-tegra/include/mach/usb_phy.h b/arch/arm/mach-tegra/include/mach/usb_phy.h index 2fe707070307..71f1b9fa76db 100644 --- a/arch/arm/mach-tegra/include/mach/usb_phy.h +++ b/arch/arm/mach-tegra/include/mach/usb_phy.h @@ -32,6 +32,11 @@ struct tegra_utmip_config { u8 xcvr_lsrslew; }; +struct tegra_ulpi_config { + int reset_gpio; + const char *clk; +}; + enum tegra_usb_phy_port_speed { TEGRA_USB_PHY_PORT_SPEED_FULL = 0, TEGRA_USB_PHY_PORT_SPEED_LOW, @@ -55,16 +60,16 @@ struct tegra_usb_phy { int freq_sel; void __iomem *regs; void __iomem *pad_regs; + struct clk *clk; struct clk *pll_u; struct clk *pad_clk; enum tegra_usb_phy_mode mode; - struct tegra_utmip_config *config; + void *config; struct tegra_utmip_context context; }; struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs, - struct tegra_utmip_config *config, - enum tegra_usb_phy_mode phy_mode); + void *config, enum tegra_usb_phy_mode phy_mode); int tegra_usb_phy_power_on(struct tegra_usb_phy *phy); diff --git a/arch/arm/mach-tegra/usb_phy.c b/arch/arm/mach-tegra/usb_phy.c index fe3b7f8ecff2..7785b1ee41ec 100644 --- a/arch/arm/mach-tegra/usb_phy.c +++ b/arch/arm/mach-tegra/usb_phy.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -31,10 +32,23 @@ #define USB_USBSTS 0x144 #define USB_USBSTS_PCI (1 << 2) +#define ULPI_VIEWPORT 0x170 +#define ULPI_WAKEUP (1 << 31) +#define ULPI_RUN (1 << 30) +#define ULPI_RD_RW_WRITE (1 << 29) +#define ULPI_RD_RW_READ (0 << 29) +#define ULPI_PORT(x) (((x) & 0x7) << 24) +#define ULPI_ADDR(x) (((x) & 0xff) << 16) +#define ULPI_DATA_RD(x) (((x) & 0xff) << 8) +#define ULPI_DATA_WR(x) (((x) & 0xff) << 0) + #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) @@ -47,7 +61,9 @@ #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 USB1_LEGACY_CTRL 0x410 @@ -59,6 +75,18 @@ #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) @@ -446,11 +474,125 @@ static void utmi_phy_power_off(struct tegra_usb_phy *phy) utmip_pad_power_off(phy); } +static void ulpi_viewport_write(struct tegra_usb_phy *phy, u8 addr, u8 data) +{ + unsigned long val; + void __iomem *base = phy->regs; + + val = ULPI_RUN | ULPI_RD_RW_WRITE | ULPI_PORT(0); + val |= ULPI_ADDR(addr) | ULPI_DATA_WR(data); + writel(val, base + ULPI_VIEWPORT); + + if (utmi_wait_register(base + ULPI_VIEWPORT, ULPI_RUN, 0)) + pr_err("%s: timeout accessing ulpi phy\n", __func__); +} + +static void ulpi_phy_power_on(struct tegra_usb_phy *phy) +{ + 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_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); + + val = ULPI_WAKEUP | ULPI_RD_RW_WRITE | ULPI_PORT(0); + writel(val, base + ULPI_VIEWPORT); + + if (utmi_wait_register(base + ULPI_VIEWPORT, ULPI_WAKEUP, 0)) { + pr_err("%s: timeout waiting for ulpi phy wakeup\n", __func__); + return; + } + + /* Fix VbusInvalid due to floating VBUS */ + ulpi_viewport_write(phy, 0x08, 0x40); + ulpi_viewport_write(phy, 0x0B, 0x80); + + 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); +} + +static void ulpi_phy_power_off(struct tegra_usb_phy *phy) +{ + unsigned long val; + void __iomem *base = phy->regs; + + /* Programming the ULPI register function control */ + ulpi_viewport_write(phy, 0x04, 0x4D); + + /* Resetting the ULPI register IndicatorPassThru */ + ulpi_viewport_write(phy, 0x09, 0x40); + + /* USB Interrupt Rising - making sure vbus comparator and id are off */ + ulpi_viewport_write(phy, 0x0D, 0x00); + + /* USB Interrupt Falling */ + ulpi_viewport_write(phy, 0x10, 0x00); + + /* Carkit Control */ + ulpi_viewport_write(phy, 0x19, 0x00); + + /* Disabling ID float Rise/Fall (Carkit Enable) */ + ulpi_viewport_write(phy, 0x1D, 0x00); + + /* USB I/O and power */ + ulpi_viewport_write(phy, 0x39, 0x00); + + /* 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); +} + struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs, - struct tegra_utmip_config *config, - enum tegra_usb_phy_mode phy_mode) + void *config, enum tegra_usb_phy_mode phy_mode) { struct tegra_usb_phy *phy; + struct tegra_ulpi_config *ulpi_config; unsigned long parent_rate; int freq_sel; int err; @@ -465,8 +607,14 @@ struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs, phy->context.valid = false; phy->mode = phy_mode; - if (!phy->config) - phy->config = &utmip_default[instance]; + if (!phy->config) { + if (instance == 1) { + pr_err("%s: ulpi phy configuration missing", __func__); + goto err0; + } else { + phy->config = &utmip_default[instance]; + } + } phy->pll_u = clk_get_sys(NULL, "pll_u"); if (IS_ERR(phy->pll_u)) { @@ -488,8 +636,17 @@ struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs, } phy->freq_sel = freq_sel; - /* TODO usb2 ulpi */ - if (phy->instance != 1) { + if (phy->instance == 1) { + ulpi_config = 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__); + goto err1; + } + tegra_gpio_enable(ulpi_config->reset_gpio); + gpio_request(ulpi_config->reset_gpio, "ulpi_phy_reset_b"); + gpio_direction_output(ulpi_config->reset_gpio, 0); + } else { err = utmip_pad_open(phy); if (err < 0) goto err1; @@ -507,8 +664,9 @@ err0: int tegra_usb_phy_power_on(struct tegra_usb_phy *phy) { - /* TODO usb2 ulpi */ - if (phy->instance != 1) + if (phy->instance == 1) + ulpi_phy_power_on(phy); + else utmi_phy_power_on(phy); return 0; @@ -516,8 +674,9 @@ int tegra_usb_phy_power_on(struct tegra_usb_phy *phy) int tegra_usb_phy_power_off(struct tegra_usb_phy *phy) { - /* TODO usb2 ulpi */ - if (phy->instance != 1) + if (phy->instance == 1) + ulpi_phy_power_off(phy); + else utmi_phy_power_off(phy); return 0; @@ -541,7 +700,9 @@ int tegra_usb_phy_clk_enable(struct tegra_usb_phy *phy) int tegra_usb_phy_close(struct tegra_usb_phy *phy) { - if (phy->instance != 1) + if (phy->instance == 1) + clk_put(phy->clk); + else utmip_pad_close(phy); clk_disable(phy->pll_u); clk_put(phy->pll_u); -- 2.34.1