summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/usb_phy.c
diff options
context:
space:
mode:
authorBenoit Goby <benoit@android.com>2010-09-02 17:47:06 -0700
committerColin Cross <ccross@android.com>2010-10-06 16:28:23 -0700
commitb3661dcd2ea92ee1f81e64565e44d5afb32c9923 (patch)
treedbe43f8ec9eeb2e479564e53247058d0084866b9 /arch/arm/mach-tegra/usb_phy.c
parentcf99404c25dd0be8c6fef97ac6f44ac5728b69cd (diff)
[ARM] tegra: usb_phy: Add support for usb2 ulpi external phy
Change-Id: Ie2ed0d22abae1319996fe0a6caf28ec7d7e4313d Signed-off-by: Benoit Goby <benoit@android.com>
Diffstat (limited to 'arch/arm/mach-tegra/usb_phy.c')
-rw-r--r--arch/arm/mach-tegra/usb_phy.c183
1 files changed, 172 insertions, 11 deletions
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 <linux/err.h>
#include <linux/platform_device.h>
#include <linux/io.h>
+#include <linux/gpio.h>
#include <asm/mach-types.h>
#include <mach/usb_phy.h>
#include <mach/iomap.h>
@@ -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);