Merge commit 'main-jb-2012.08.03-B4' into t114-0806
[linux-2.6.git] / arch / arm / mach-tegra / tegra3_usb_phy.c
index ed5931a..e609720 100644 (file)
 #include <linux/clk.h>
 #include <linux/regulator/consumer.h>
 #include <linux/platform_data/tegra_usb.h>
+#include <mach/clk.h>
 #include <mach/iomap.h>
 #include <mach/pinmux.h>
 #include <mach/pinmux-tegra30.h>
 #include "tegra_usb_phy.h"
+#include "gpio-names.h"
 #include "fuse.h"
 
 #define USB_USBCMD             0x130
 #define          XCVR_SETUP_MSB_MASK   0x70
 #define   XCVR_SETUP_LSB_MAX_VAL       0xF
 
+#define APB_MISC_GP_OBSCTRL_0  0x818
+#define APB_MISC_GP_OBSDATA_0  0x81c
+
+/* ULPI GPIO */
+#define ULPI_STP       TEGRA_GPIO_PY3
+#define ULPI_DIR       TEGRA_GPIO_PY1
+#define ULPI_D0                TEGRA_GPIO_PO1
+#define ULPI_D1                TEGRA_GPIO_PO2
+
 /* These values (in milli second) are taken from the battery charging spec */
 #define TDP_SRC_ON_MS   100
 #define TDPSRC_CON_MS   40
 #define PHY_DBG(stuff...)      do {} while (0)
 #endif
 
+/* define HSIC phy params */
+#define HSIC_SYNC_START_DELAY          9
+#define HSIC_IDLE_WAIT_DELAY           17
+#define HSIC_ELASTIC_UNDERRUN_LIMIT    16
+#define HSIC_ELASTIC_OVERRUN_LIMIT     16
+
 static u32 utmip_rctrl_val, utmip_tctrl_val;
 static DEFINE_SPINLOCK(utmip_pad_lock);
 static int utmip_pad_count;
@@ -632,12 +649,6 @@ static void utmip_setup_pmc_wake_detect(struct tegra_usb_phy *phy)
        val |= UTMIP_LINEVAL_WALK_EN(inst);
        writel(val, pmc_base + PMC_SLEEPWALK_CFG);
 
-       /* Clear the walk pointers and wake alarm */
-       val = readl(pmc_base + PMC_TRIGGERS);
-       val |= UTMIP_CLR_WAKE_ALARM(inst) | UTMIP_CLR_WALK_PTR(inst);
-       writel(val, pmc_base + PMC_TRIGGERS);
-
-
        /* Capture FS/LS pad configurations */
        pmc_pad_cfg_val = readl(pmc_base + PMC_PAD_CFG);
        val = readl(pmc_base + PMC_TRIGGERS);
@@ -716,10 +727,6 @@ static void utmip_phy_disable_pmc_bus_ctrl(struct tegra_usb_phy *phy)
        val |= UTMIP_WAKE_VAL(inst, WAKE_VAL_NONE);
        writel(val, pmc_base + PMC_SLEEP_CFG);
 
-       val = readl(pmc_base + PMC_TRIGGERS);
-       val |= UTMIP_CLR_WAKE_ALARM(inst) | UTMIP_CLR_WALK_PTR(inst);
-       writel(val, pmc_base + PMC_TRIGGERS);
-
        val = readl(base + UTMIP_PMC_WAKEUP0);
        val &= ~EVENT_INT_ENB;
        writel(val, base + UTMIP_PMC_WAKEUP0);
@@ -739,11 +746,16 @@ static void utmip_phy_disable_pmc_bus_ctrl(struct tegra_usb_phy *phy)
        val |= (USBOP_VAL_PD(inst) | USBON_VAL_PD(inst));
        writel(val, pmc_base + PMC_USB_AO);
 
+       val = readl(pmc_base + PMC_TRIGGERS);
+       val |= UTMIP_CLR_WALK_PTR(inst);
+       val |= UTMIP_CLR_WAKE_ALARM(inst);
+       writel(val, pmc_base + PMC_TRIGGERS);
+
        phy->remote_wakeup = false;
        PHY_DBG("%s DISABLE_PMC inst = %d\n", __func__, inst);
 }
 
-bool utmi_phy_remotewake_detected(struct tegra_usb_phy *phy)
+static bool utmi_phy_remotewake_detected(struct tegra_usb_phy *phy)
 {
        void __iomem *pmc_base = IO_ADDRESS(TEGRA_PMC_BASE);
        void __iomem *base = phy->regs;
@@ -761,8 +773,7 @@ bool utmi_phy_remotewake_detected(struct tegra_usb_phy *phy)
                        writel(val, pmc_base + PMC_SLEEP_CFG);
 
                        val = readl(pmc_base + PMC_TRIGGERS);
-                       val |= UTMIP_CLR_WAKE_ALARM(inst) |
-                               UTMIP_CLR_WALK_PTR(inst);
+                       val |= UTMIP_CLR_WAKE_ALARM(inst);
                        writel(val, pmc_base + PMC_TRIGGERS);
 
                        val = readl(base + UTMIP_PMC_WAKEUP0);
@@ -984,7 +995,8 @@ static int usb_phy_bringup_host_controller(struct tegra_usb_phy *phy)
                /* Program the field PTC based on the saved speed mode */
                val = readl(base + USB_PORTSC);
                val &= ~USB_PORTSC_PTC(~0);
-               if (phy->port_speed == USB_PHY_PORT_SPEED_HIGH)
+               if ((phy->port_speed == USB_PHY_PORT_SPEED_HIGH) ||
+                       (phy->pdata->phy_intf == TEGRA_USB_PHY_INTF_HSIC))
                        val |= USB_PORTSC_PTC(5);
                else if (phy->port_speed == USB_PHY_PORT_SPEED_FULL)
                        val |= USB_PORTSC_PTC(6);
@@ -1080,8 +1092,9 @@ static unsigned int utmi_phy_xcvr_setup_value(struct tegra_usb_phy *phy)
        if (cfg->xcvr_use_fuses) {
                val = XCVR_SETUP(tegra_fuse_readl(FUSE_USB_CALIB_0));
                if (cfg->xcvr_use_lsb) {
-                       val = min(((val & XCVR_SETUP_LSB_MASK) + cfg->xcvr_setup_offset),
-                                       XCVR_SETUP_LSB_MAX_VAL);
+                       val = min((unsigned int) ((val & XCVR_SETUP_LSB_MASK)
+                               + cfg->xcvr_setup_offset),
+                               (unsigned int) XCVR_SETUP_LSB_MAX_VAL);
                        val |= (cfg->xcvr_setup & XCVR_SETUP_MSB_MASK);
                } else {
                        if (cfg->xcvr_setup_offset <= UTMIP_XCVR_MAX_OFFSET)
@@ -1217,11 +1230,13 @@ static int utmi_phy_irq(struct tegra_usb_phy *phy)
        void __iomem *base = phy->regs;
        unsigned long val = 0;
 
-       DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
-       DBG("USB_USBSTS[0x%x] USB_PORTSC[0x%x]\n",
+       if (phy->phy_clk_on) {
+               DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
+               DBG("USB_USBSTS[0x%x] USB_PORTSC[0x%x]\n",
                        readl(base + USB_USBSTS), readl(base + USB_PORTSC));
-       DBG("USB_USBMODE[0x%x] USB_USBCMD[0x%x]\n",
+               DBG("USB_USBMODE[0x%x] USB_USBCMD[0x%x]\n",
                        readl(base + USB_USBMODE), readl(base + USB_USBCMD));
+       }
 
        usb_phy_fence_read(phy);
        /* check if there is any remote wake event */
@@ -1241,6 +1256,8 @@ static int utmi_phy_irq(struct tegra_usb_phy *phy)
                        val = readl(base + USB_PORTSC);
                        val &= ~(USB_PORTSC_WKCN | USB_PORTSC_RWC_BITS);
                        writel(val , (base + USB_PORTSC));
+               } else if (!phy->phy_clk_on) {
+                       return IRQ_NONE;
                }
        }
 
@@ -1352,6 +1369,17 @@ static int utmi_phy_post_resume(struct tegra_usb_phy *phy)
        return 0;
 }
 
+static int phy_post_suspend(struct tegra_usb_phy *phy)
+{
+
+       DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
+       /* Need a 4ms delay for controller to suspend */
+       mdelay(4);
+
+       return 0;
+
+}
+
 static int utmi_phy_pre_resume(struct tegra_usb_phy *phy, bool remote_wakeup)
 {
        unsigned long val;
@@ -1625,10 +1653,18 @@ static void utmi_phy_restore_start(struct tegra_usb_phy *phy)
 
        DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
        val = readl(pmc_base + UTMIP_UHSIC_STATUS);
-       /* check whether we wake up from the remote resume */
+       /* Check whether we wake up from the remote resume.
+          For lp1 case, pmc is not responsible for waking the
+          system, it's the flow controller and hence
+          UTMIP_WALK_PTR_VAL(inst) will return 0.
+          Also, for lp1 case phy->remote_wakeup will already be set
+          to true by utmi_phy_irq() when the remote wakeup happens.
+          Hence change the logic in the else part to enter only
+          if phy->remote_wakeup is not set to true by the
+          utmi_phy_irq(). */
        if (UTMIP_WALK_PTR_VAL(inst) & val) {
                phy->remote_wakeup = true;
-       } else {
+       } else if(!phy->remote_wakeup) {
                if (!((UTMIP_USBON_VAL(phy->inst) |
                        UTMIP_USBOP_VAL(phy->inst)) & val)) {
                                utmip_phy_disable_pmc_bus_ctrl(phy);
@@ -1731,7 +1767,7 @@ static int utmi_phy_resume(struct tegra_usb_phy *phy)
        return status;
 }
 
-bool utmi_phy_charger_detect(struct tegra_usb_phy *phy)
+static bool utmi_phy_charger_detect(struct tegra_usb_phy *phy)
 {
        unsigned long val;
        void __iomem *base = phy->regs;
@@ -1792,6 +1828,31 @@ static void uhsic_powerup_pmc_wake_detect(struct tegra_usb_phy *phy)
        mdelay(1);
 }
 
+static void uhsic_powerdown_pmc_wake_detect(struct tegra_usb_phy *phy)
+{
+       unsigned long val;
+       void __iomem *pmc_base = IO_ADDRESS(TEGRA_PMC_BASE);
+
+       DBG("%s:%d\n", __func__, __LINE__);
+
+       /* turn off pad detectors for HSIC*/
+       val = readl(pmc_base + PMC_USB_AO);
+       val |= (HSIC_RESERVED_P0 | STROBE_VAL_PD_P0 | DATA_VAL_PD_P0);
+       writel(val, pmc_base + PMC_USB_AO);
+
+       /* enable pull downs on HSIC PMC */
+       val = UHSIC_STROBE_RPD_A | UHSIC_DATA_RPD_A | UHSIC_STROBE_RPD_B |
+               UHSIC_DATA_RPD_B | UHSIC_STROBE_RPD_C | UHSIC_DATA_RPD_C |
+               UHSIC_STROBE_RPD_D | UHSIC_DATA_RPD_D;
+       writel(val, pmc_base + PMC_SLEEPWALK_UHSIC);
+
+       /* Turn over pad configuration to PMC */
+       val = readl(pmc_base + PMC_SLEEP_CFG);
+       val &= ~UHSIC_WAKE_VAL_P0(~0);
+       val |= UHSIC_WAKE_VAL_P0(WAKE_VAL_NONE) | UHSIC_MASTER_ENABLE_P0;
+       writel(val, pmc_base + PMC_SLEEP_CFG);
+}
+
 static void uhsic_setup_pmc_wake_detect(struct tegra_usb_phy *phy)
 {
        unsigned long val;
@@ -1826,6 +1887,30 @@ static void uhsic_setup_pmc_wake_detect(struct tegra_usb_phy *phy)
        val |= UHSIC_PWR;
        writel(val, pmc_base + PMC_UTMIP_MASTER_CONFIG);
 
+       /* Make sure nothing is happening on the line with respect to PMC */
+       val = readl(pmc_base + PMC_UTMIP_UHSIC_FAKE);
+       val &= ~UHSIC_STROBE_VAL;
+       val &= ~UHSIC_DATA_VAL;
+       writel(val, pmc_base + PMC_UTMIP_UHSIC_FAKE);
+
+       /* Clear walk enable */
+       val = readl(pmc_base + PMC_SLEEPWALK_CFG);
+       val &= ~UHSIC_LINEVAL_WALK_EN;
+       writel(val, pmc_base + PMC_SLEEPWALK_CFG);
+
+       /* Make sure wake value for line is none */
+       val = readl(pmc_base + PMC_SLEEP_CFG);
+       val &= ~UHSIC_WAKE_VAL(WAKE_VAL_ANY);
+       val |= UHSIC_WAKE_VAL(WAKE_VAL_NONE);
+       writel(val, pmc_base + PMC_SLEEP_CFG);
+
+       /* turn on pad detectors */
+       val = readl(pmc_base + PMC_USB_AO);
+       val &= ~(STROBE_VAL_PD_P0 | DATA_VAL_PD_P0);
+       writel(val, pmc_base + PMC_USB_AO);
+
+       /* Add small delay before usb detectors provide stable line values */
+       udelay(1);
 
        /* Enable which type of event can trigger a walk,
        * in this case usb_line_wake */
@@ -1842,19 +1927,16 @@ static void uhsic_setup_pmc_wake_detect(struct tegra_usb_phy *phy)
        val |=  UHSIC_DATA_RPD_A;
        val &= ~UHSIC_STROBE_RPD_A;
        val |=  UHSIC_STROBE_RPU_A;
-       writel(val, pmc_base + PMC_SLEEPWALK_UHSIC);
 
        val &= ~UHSIC_DATA_RPD_B;
        val |=  UHSIC_DATA_RPU_B;
        val &= ~UHSIC_STROBE_RPU_B;
        val |=  UHSIC_STROBE_RPD_B;
-       writel(val, pmc_base + PMC_SLEEPWALK_UHSIC);
 
        val &= ~UHSIC_DATA_RPD_C;
        val |=  UHSIC_DATA_RPU_C;
        val &= ~UHSIC_STROBE_RPU_C;
        val |=  UHSIC_STROBE_RPD_C;
-       writel(val, pmc_base + PMC_SLEEPWALK_UHSIC);
 
        val &= ~UHSIC_DATA_RPD_D;
        val |=  UHSIC_DATA_RPU_D;
@@ -1862,19 +1944,21 @@ static void uhsic_setup_pmc_wake_detect(struct tegra_usb_phy *phy)
        val |=  UHSIC_STROBE_RPD_D;
        writel(val, pmc_base + PMC_SLEEPWALK_UHSIC);
 
-       /* turn on pad detectors */
-       val = readl(pmc_base + PMC_USB_AO);
-       val &= ~(STROBE_VAL_PD_P0 | DATA_VAL_PD_P0);
-       writel(val, pmc_base + PMC_USB_AO);
-       /* Add small delay before usb detectors provide stable line values */
-       udelay(1);
-
        phy->remote_wakeup = false;
 
-       /* Turn over pad configuration to PMC  for line wake events*/
+       /* Setting Wake event*/
        val = readl(pmc_base + PMC_SLEEP_CFG);
-       val &= ~UHSIC_WAKE_VAL(~0);
+       val &= ~UHSIC_WAKE_VAL(WAKE_VAL_ANY);
        val |= UHSIC_WAKE_VAL(WAKE_VAL_SD10);
+       writel(val, pmc_base + PMC_SLEEP_CFG);
+
+       /* Clear the walk pointers and wake alarm */
+       val = readl(pmc_base + PMC_TRIGGERS);
+       val |= UHSIC_CLR_WAKE_ALARM_P0 | UHSIC_CLR_WALK_PTR_P0;
+       writel(val, pmc_base + PMC_TRIGGERS);
+
+       /* Turn over pad configuration to PMC  for line wake events*/
+       val = readl(pmc_base + PMC_SLEEP_CFG);
        val |= UHSIC_MASTER_ENABLE;
        writel(val, pmc_base + PMC_SLEEP_CFG);
 
@@ -1893,14 +1977,10 @@ static void uhsic_phy_disable_pmc_bus_ctrl(struct tegra_usb_phy *phy)
 
        DBG("%s (%d)\n", __func__, __LINE__);
        val = readl(pmc_base + PMC_SLEEP_CFG);
-       val &= ~UHSIC_WAKE_VAL(0x0);
+       val &= ~UHSIC_WAKE_VAL(WAKE_VAL_ANY);
        val |= UHSIC_WAKE_VAL(WAKE_VAL_NONE);
        writel(val, pmc_base + PMC_SLEEP_CFG);
 
-       val = readl(pmc_base + PMC_TRIGGERS);
-       val |= UHSIC_CLR_WAKE_ALARM_P0 | UHSIC_CLR_WALK_PTR_P0;
-       writel(val, pmc_base + PMC_TRIGGERS);
-
        val = readl(base + UHSIC_PMC_WAKEUP0);
        val &= ~EVENT_INT_ENB;
        writel(val, base + UHSIC_PMC_WAKEUP0);
@@ -1915,6 +1995,10 @@ static void uhsic_phy_disable_pmc_bus_ctrl(struct tegra_usb_phy *phy)
        val |= (STROBE_VAL_PD_P0 | DATA_VAL_PD_P0);
        writel(val, pmc_base + PMC_USB_AO);
 
+       val = readl(pmc_base + PMC_TRIGGERS);
+       val |= (UHSIC_CLR_WALK_PTR_P0 | UHSIC_CLR_WAKE_ALARM_P0);
+       writel(val, pmc_base + PMC_TRIGGERS);
+
        phy->remote_wakeup = false;
 }
 
@@ -1929,12 +2013,12 @@ static bool uhsic_phy_remotewake_detected(struct tegra_usb_phy *phy)
                val = readl(pmc_base + UTMIP_UHSIC_STATUS);
                if (UHSIC_WAKE_ALARM & val) {
                        val = readl(pmc_base + PMC_SLEEP_CFG);
-                       val &= ~UHSIC_WAKE_VAL(0x0);
+                       val &= ~UHSIC_WAKE_VAL(WAKE_VAL_ANY);
                        val |= UHSIC_WAKE_VAL(WAKE_VAL_NONE);
                        writel(val, pmc_base + PMC_SLEEP_CFG);
 
                        val = readl(pmc_base + PMC_TRIGGERS);
-                       val |= UHSIC_CLR_WAKE_ALARM_P0 | UHSIC_CLR_WALK_PTR_P0;
+                       val |= UHSIC_CLR_WAKE_ALARM_P0;
                        writel(val, pmc_base + PMC_TRIGGERS);
 
                        val = readl(base + UHSIC_PMC_WAKEUP0);
@@ -1984,6 +2068,7 @@ static void uhsic_phy_restore_start(struct tegra_usb_phy *phy)
        /* check whether we wake up from the remote resume */
        if (UHSIC_WALK_PTR_VAL & val) {
                phy->remote_wakeup = true;
+               pr_info("%s: uhsic remote wakeup detected\n", __func__);
        } else {
                if (!((UHSIC_STROBE_VAL_P0 | UHSIC_DATA_VAL_P0) & val)) {
                                uhsic_phy_disable_pmc_bus_ctrl(phy);
@@ -2001,7 +2086,7 @@ static void uhsic_phy_restore_end(struct tegra_usb_phy *phy)
 
        unsigned long val;
        void __iomem *base = phy->regs;
-       int wait_time_us = 3000; /* FPR should be set by this time */
+       int wait_time_us = 25000; /* FPR should be set by this time */
 
        DBG("%s(%d)\n", __func__, __LINE__);
 
@@ -2017,7 +2102,8 @@ static void uhsic_phy_restore_end(struct tegra_usb_phy *phy)
                                return;
                        }
                        wait_time_us--;
-               } while (!(val & USB_PORTSC_RESUME));
+               } while (val & (USB_PORTSC_RESUME | USB_PORTSC_SUSP));
+
                /* wait for 25 ms to port resume complete */
                msleep(25);
                /* disable PMC master control */
@@ -2047,10 +2133,58 @@ static void uhsic_phy_restore_end(struct tegra_usb_phy *phy)
        }
 }
 
+static int hsic_rail_enable(struct tegra_usb_phy *phy)
+{
+       int ret;
+
+       if (phy->hsic_reg == NULL) {
+               phy->hsic_reg = regulator_get(NULL, "avdd_hsic");
+               if (IS_ERR_OR_NULL(phy->hsic_reg)) {
+                       pr_err("HSIC: Could not get regulator avdd_hsic\n");
+                       phy->hsic_reg = NULL;
+                       return PTR_ERR(phy->hsic_reg);
+               }
+       }
+
+       ret = regulator_enable(phy->hsic_reg);
+       if (ret < 0) {
+               pr_err("%s avdd_hsic could not be enabled\n", __func__);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int hsic_rail_disable(struct tegra_usb_phy *phy)
+{
+       int ret;
+
+       if (phy->hsic_reg == NULL) {
+               pr_warn("%s: unbalanced disable\n", __func__);
+               return -EIO;
+       }
+
+       ret = regulator_disable(phy->hsic_reg);
+       if (ret < 0) {
+               pr_err("HSIC regulator avdd_hsic cannot be disabled\n");
+               return ret;
+       }
+
+       return 0;
+}
+
 static int uhsic_phy_open(struct tegra_usb_phy *phy)
 {
        unsigned long parent_rate;
        int i;
+       int ret;
+
+       phy->hsic_reg = NULL;
+       ret = hsic_rail_enable(phy);
+       if (ret < 0) {
+               pr_err("%s avdd_hsic could not be enabled\n", __func__);
+               return ret;
+       }
 
        DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
        parent_rate = clk_get_rate(clk_get_parent(phy->pllu_clk));
@@ -2070,6 +2204,18 @@ static int uhsic_phy_open(struct tegra_usb_phy *phy)
        return 0;
 }
 
+static void uhsic_phy_close(struct tegra_usb_phy *phy)
+{
+       int ret;
+
+       DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
+       uhsic_powerdown_pmc_wake_detect(phy);
+
+       ret = hsic_rail_disable(phy);
+       if (ret < 0)
+               pr_err("%s avdd_hsic could not be disabled\n", __func__);
+}
+
 static int uhsic_phy_irq(struct tegra_usb_phy *phy)
 {
        usb_phy_fence_read(phy);
@@ -2083,7 +2229,6 @@ static int uhsic_phy_power_on(struct tegra_usb_phy *phy)
 {
        unsigned long val;
        void __iomem *base = phy->regs;
-       struct tegra_hsic_config *config = &phy->pdata->u_cfg.hsic;
 
        DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
 
@@ -2094,29 +2239,28 @@ static int uhsic_phy_power_on(struct tegra_usb_phy *phy)
        }
 
        val = readl(base + UHSIC_PADS_CFG1);
-       val &= ~(UHSIC_PD_BG | UHSIC_PD_TX | UHSIC_PD_TRK | UHSIC_PD_RX |
+       val &= ~(UHSIC_PD_BG | UHSIC_PD_TRK | UHSIC_PD_RX |
                        UHSIC_PD_ZI | UHSIC_RPD_DATA | UHSIC_RPD_STROBE);
-       val |= UHSIC_RX_SEL;
+       val |= (UHSIC_RX_SEL | UHSIC_PD_TX);
        writel(val, base + UHSIC_PADS_CFG1);
-       udelay(2);
 
        val = readl(base + USB_SUSP_CTRL);
        val |= UHSIC_RESET;
        writel(val, base + USB_SUSP_CTRL);
-       udelay(30);
+       udelay(1);
 
        val = readl(base + USB_SUSP_CTRL);
        val |= UHSIC_PHY_ENABLE;
        writel(val, base + USB_SUSP_CTRL);
 
        val = readl(base + UHSIC_HSRX_CFG0);
-       val |= UHSIC_IDLE_WAIT(config->idle_wait_delay);
-       val |= UHSIC_ELASTIC_UNDERRUN_LIMIT(config->elastic_underrun_limit);
-       val |= UHSIC_ELASTIC_OVERRUN_LIMIT(config->elastic_overrun_limit);
+       val |= UHSIC_IDLE_WAIT(HSIC_IDLE_WAIT_DELAY);
+       val |= UHSIC_ELASTIC_UNDERRUN_LIMIT(HSIC_ELASTIC_UNDERRUN_LIMIT);
+       val |= UHSIC_ELASTIC_OVERRUN_LIMIT(HSIC_ELASTIC_OVERRUN_LIMIT);
        writel(val, base + UHSIC_HSRX_CFG0);
 
        val = readl(base + UHSIC_HSRX_CFG1);
-       val |= UHSIC_HS_SYNC_START_DLY(config->sync_start_delay);
+       val |= UHSIC_HS_SYNC_START_DLY(HSIC_SYNC_START_DELAY);
        writel(val, base + UHSIC_HSRX_CFG1);
 
        /* WAR HSIC TX */
@@ -2142,7 +2286,11 @@ static int uhsic_phy_power_on(struct tegra_usb_phy *phy)
        val = readl(base + USB_SUSP_CTRL);
        val &= ~(UHSIC_RESET);
        writel(val, base + USB_SUSP_CTRL);
-       udelay(2);
+       udelay(1);
+
+       val = readl(base + UHSIC_PADS_CFG1);
+       val &= ~(UHSIC_PD_TX);
+       writel(val, base + UHSIC_PADS_CFG1);
 
        val = readl(base + USB_USBMODE);
        val |= USB_USBMODE_HOST;
@@ -2182,6 +2330,15 @@ static int uhsic_phy_power_on(struct tegra_usb_phy *phy)
        phy->phy_clk_on = true;
        phy->hw_accessible = true;
 
+       if (phy->pmc_sleepwalk) {
+               DBG("%s(%d) inst:[%d] restore phy\n", __func__, __LINE__,
+                                       phy->inst);
+               uhsic_phy_restore_start(phy);
+               usb_phy_bringup_host_controller(phy);
+               uhsic_phy_restore_end(phy);
+               phy->pmc_sleepwalk = false;
+       }
+
        return 0;
 }
 
@@ -2203,27 +2360,32 @@ static int uhsic_phy_power_off(struct tegra_usb_phy *phy)
        /* Disable interrupts */
        writel(0, base + USB_USBINTR);
 
-       uhsic_setup_pmc_wake_detect(phy);
+       if (phy->pmc_sleepwalk == false) {
+               uhsic_setup_pmc_wake_detect(phy);
+               phy->pmc_sleepwalk = true;
+       }
 
        val = readl(base + HOSTPC1_DEVLC);
        val |= HOSTPC1_DEVLC_PHCD;
        writel(val, base + HOSTPC1_DEVLC);
 
+       /* Remove power downs for HSIC from PADS CFG1 register */
+       val = readl(base + UHSIC_PADS_CFG1);
+       val |= (UHSIC_PD_BG |UHSIC_PD_TRK | UHSIC_PD_RX |
+                       UHSIC_PD_ZI | UHSIC_PD_TX);
+       writel(val, base + UHSIC_PADS_CFG1);
        phy->phy_clk_on = false;
        phy->hw_accessible = false;
 
        return 0;
 }
 
-int uhsic_phy_bus_port_power(struct tegra_usb_phy *phy)
+static int uhsic_phy_bus_port_power(struct tegra_usb_phy *phy)
 {
        unsigned long val;
        void __iomem *base = phy->regs;
 
        DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
-       val = readl(base + UHSIC_STAT_CFG0);
-       val &= ~UHSIC_CONNECT_DETECT;
-       writel(val, base + UHSIC_STAT_CFG0);
 
        val = readl(base + USB_USBMODE);
        val |= USB_USBMODE_HOST;
@@ -2248,13 +2410,10 @@ int uhsic_phy_bus_port_power(struct tegra_usb_phy *phy)
 
        val = readl(base + UHSIC_PADS_CFG1);
        val &= ~UHSIC_RPD_STROBE;
-       /* safe to enable RPU on STROBE at all times during idle */
-       val |= UHSIC_RPU_STROBE;
        writel(val, base + UHSIC_PADS_CFG1);
 
-       val = readl(base + USB_USBCMD);
-       val &= ~USB_USBCMD_RS;
-       writel(val, base + USB_USBCMD);
+       if (phy->pdata->ops && phy->pdata->ops->port_power)
+               phy->pdata->ops->port_power();
 
        if (usb_phy_reg_status_wait(base + UHSIC_STAT_CFG0,
                        UHSIC_CONNECT_DETECT, UHSIC_CONNECT_DETECT, 25000)) {
@@ -2358,10 +2517,6 @@ int uhsic_phy_resume(struct tegra_usb_phy *phy)
 {
        DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
 
-       uhsic_phy_restore_start(phy);
-       usb_phy_bringup_host_controller(phy);
-       uhsic_phy_restore_end(phy);
-
        return 0;
 }
 
@@ -2401,6 +2556,7 @@ static void ulpi_set_host(void __iomem *base)
        unsigned long val;
 
        val = readl(base + USB_USBMODE);
+       val &= ~USB_USBMODE_MASK;
        val |= USB_USBMODE_HOST;
        writel(val, base + USB_USBMODE);
 
@@ -2409,6 +2565,106 @@ static void ulpi_set_host(void __iomem *base)
        writel(val, base + HOSTPC1_DEVLC);
 }
 
+static inline void ulpi_pinmux_bypass(struct tegra_usb_phy *phy, bool enable)
+{
+       unsigned long val;
+       void __iomem *base = phy->regs;
+
+       val = readl(base + ULPI_TIMING_CTRL_0);
+
+       if (enable)
+               val |= ULPI_OUTPUT_PINMUX_BYP;
+       else
+               val &= ~ULPI_OUTPUT_PINMUX_BYP;
+
+       writel(val, base + ULPI_TIMING_CTRL_0);
+}
+
+static inline void ulpi_null_phy_set_tristate(bool enable)
+{
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+       int tristate = (enable) ? TEGRA_TRI_TRISTATE : TEGRA_TRI_NORMAL;
+
+       tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DATA0, tristate);
+       tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DATA1, tristate);
+       tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DATA2, tristate);
+       tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DATA3, tristate);
+       tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DATA4, tristate);
+       tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DATA5, tristate);
+       tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DATA6, tristate);
+       tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DATA7, tristate);
+       tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_NXT, tristate);
+
+       if (enable)
+               tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DIR, tristate);
+#endif
+}
+
+static void ulpi_null_phy_obs_read(void)
+{
+       static void __iomem *apb_misc;
+       unsigned slv0_obs, s2s_obs;
+
+       if (!apb_misc)
+               apb_misc = ioremap(TEGRA_APB_MISC_BASE, TEGRA_APB_MISC_SIZE);
+
+       writel(0x80d1003c, apb_misc + APB_MISC_GP_OBSCTRL_0);
+       slv0_obs = readl(apb_misc + APB_MISC_GP_OBSDATA_0);
+
+       writel(0x80d10040, apb_misc + APB_MISC_GP_OBSCTRL_0);
+       s2s_obs = readl(apb_misc + APB_MISC_GP_OBSDATA_0);
+
+       pr_debug("slv0 obs: %08x\ns2s obs: %08x\n", slv0_obs, s2s_obs);
+}
+
+static const struct gpio ulpi_gpios[] = {
+       {ULPI_STP, GPIOF_IN, "ULPI_STP"},
+       {ULPI_DIR, GPIOF_OUT_INIT_LOW, "ULPI_DIR"},
+       {ULPI_D0, GPIOF_OUT_INIT_LOW, "ULPI_D0"},
+       {ULPI_D1, GPIOF_OUT_INIT_LOW, "ULPI_D1"},
+};
+
+static int ulpi_null_phy_open(struct tegra_usb_phy *phy)
+{
+       struct tegra_ulpi_config *config = &phy->pdata->u_cfg.ulpi;
+       int ret;
+
+       DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
+
+       ret = gpio_request_array(ulpi_gpios, ARRAY_SIZE(ulpi_gpios));
+       if (ret)
+               return ret;
+
+       if (gpio_is_valid(config->phy_restore_gpio)) {
+               ret = gpio_request(config->phy_restore_gpio, "phy_restore");
+               if (ret)
+                       goto err_gpio_free;
+
+               gpio_direction_input(config->phy_restore_gpio);
+       }
+
+       tegra_periph_reset_assert(phy->ctrlr_clk);
+       udelay(10);
+       tegra_periph_reset_deassert(phy->ctrlr_clk);
+
+       return 0;
+
+err_gpio_free:
+       gpio_free_array(ulpi_gpios, ARRAY_SIZE(ulpi_gpios));
+       return ret;
+}
+
+static void ulpi_null_phy_close(struct tegra_usb_phy *phy)
+{
+       struct tegra_ulpi_config *config = &phy->pdata->u_cfg.ulpi;
+
+       DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
+
+       if (gpio_is_valid(config->phy_restore_gpio))
+               gpio_free(config->phy_restore_gpio);
+
+       gpio_free_array(ulpi_gpios, ARRAY_SIZE(ulpi_gpios));
+}
 
 static int ulpi_null_phy_power_off(struct tegra_usb_phy *phy)
 {
@@ -2422,10 +2678,11 @@ static int ulpi_null_phy_power_off(struct tegra_usb_phy *phy)
 
        phy->phy_clk_on = false;
        phy->hw_accessible = false;
-
+       ulpi_null_phy_set_tristate(true);
        return 0;
 }
 
+/* NOTE: this function must be called before ehci reset */
 static int ulpi_null_phy_init(struct tegra_usb_phy *phy)
 {
        unsigned long val;
@@ -2446,10 +2703,20 @@ static int ulpi_null_phy_init(struct tegra_usb_phy *phy)
 
 static int ulpi_null_phy_irq(struct tegra_usb_phy *phy)
 {
+       unsigned long val;
+       void __iomem *base = phy->regs;
+
        usb_phy_fence_read(phy);
+       if (phy->bus_reseting){
+               val = readl(base + USB_USBCMD);
+               val |= USB_USBCMD_RS;
+               writel(val, base + USB_USBCMD);
+               phy->bus_reseting = false;
+       }
        return IRQ_HANDLED;
 }
 
+/* NOTE: this function must be called after ehci reset */
 static int ulpi_null_phy_cmd_reset(struct tegra_usb_phy *phy)
 {
        unsigned long val;
@@ -2471,11 +2738,64 @@ static int ulpi_null_phy_cmd_reset(struct tegra_usb_phy *phy)
        return 0;
 }
 
+static int ulpi_phy_bus_reset(struct tegra_usb_phy *phy)
+{
+       unsigned long val;
+       void __iomem *base = phy->regs;
+
+       DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
+
+       /*DISABLE RUN BIT */
+
+       val = readl(base + USB_USBCMD);
+       val &= ~USB_USBCMD_RS;
+       writel(val, base + USB_USBCMD);
+       phy->bus_reseting = true;
+
+       return 0;
+}
+
+static int ulpi_null_phy_restore(struct tegra_usb_phy *phy)
+{
+       struct tegra_ulpi_config *config = &phy->pdata->u_cfg.ulpi;
+       unsigned long timeout;
+       int ulpi_stp = ULPI_STP;
+
+       if (gpio_is_valid(config->phy_restore_gpio))
+               ulpi_stp = config->phy_restore_gpio;
+
+       /* disable ULPI pinmux bypass */
+       ulpi_pinmux_bypass(phy, false);
+
+       /* driving linstate by GPIO */
+       gpio_set_value(ULPI_D0, 0);
+       gpio_set_value(ULPI_D1, 0);
+
+       /* driving DIR high */
+       gpio_set_value(ULPI_DIR, 1);
+
+       /* remove ULPI tristate */
+       ulpi_null_phy_set_tristate(false);
+
+       /* wait for STP high */
+       timeout = jiffies + msecs_to_jiffies(25);
+
+       while (!gpio_get_value(ulpi_stp)) {
+               if (time_after(jiffies, timeout)) {
+                       pr_warn("phy restore timeout\n");
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
 static int ulpi_null_phy_lp0_resume(struct tegra_usb_phy *phy)
 {
        unsigned long val;
        void __iomem *base = phy->regs;
 
+       DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
        ulpi_null_phy_init(phy);
 
        val = readl(base + USB_USBCMD);
@@ -2487,11 +2807,6 @@ static int ulpi_null_phy_lp0_resume(struct tegra_usb_phy *phy)
                pr_err("%s: timeout waiting for reset\n", __func__);
        }
 
-       val = readl(base + USB_USBMODE);
-       val &= ~USB_USBMODE_MASK;
-       val |= USB_USBMODE_HOST;
-       writel(val, base + USB_USBMODE);
-
        ulpi_null_phy_cmd_reset(phy);
 
        val = readl(base + USB_USBCMD);
@@ -2509,10 +2824,7 @@ static int ulpi_null_phy_lp0_resume(struct tegra_usb_phy *phy)
        writel(val, base + USB_PORTSC);
        udelay(10);
 
-       /* disable ULPI pinmux bypass */
-       val = readl(base + ULPI_TIMING_CTRL_0);
-       val &= ~ULPI_OUTPUT_PINMUX_BYP;
-       writel(val, base + ULPI_TIMING_CTRL_0);
+       ulpi_null_phy_restore(phy);
 
        return 0;
 }
@@ -2522,7 +2834,6 @@ static int ulpi_null_phy_power_on(struct tegra_usb_phy *phy)
        unsigned long val;
        void __iomem *base = phy->regs;
        struct tegra_ulpi_config *config = &phy->pdata->u_cfg.ulpi;
-       static bool cold_boot = true;
 
        DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
        if (phy->phy_clk_on) {
@@ -2605,29 +2916,37 @@ static int ulpi_null_phy_power_on(struct tegra_usb_phy *phy)
        val &= ~ULPI_PADS_RESET;
        writel(val, base + USB_SUSP_CTRL);
 
-       if (cold_boot) {
+       if (!phy->ulpi_clk_padout_ena) {
                val = readl(base + ULPI_TIMING_CTRL_0);
                val |= ULPI_CLK_PADOUT_ENA;
                writel(val, base + ULPI_TIMING_CTRL_0);
-               cold_boot = false;
+               phy->ulpi_clk_padout_ena = true;
        } else {
                if (!readl(base + USB_ASYNCLISTADDR))
                        ulpi_null_phy_lp0_resume(phy);
        }
        udelay(10);
 
+       phy->bus_reseting = false;
        phy->phy_clk_on = true;
        phy->hw_accessible = true;
 
        return 0;
 }
 
-
-int ulpi_null_phy_pre_resume(struct tegra_usb_phy *phy, bool remote_wakeup)
+static int ulpi_null_phy_pre_resume(struct tegra_usb_phy *phy, bool remote_wakeup)
 {
        DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
-
+       ulpi_null_phy_obs_read();
        usb_phy_wait_for_sof(phy);
+       ulpi_null_phy_obs_read();
+       return 0;
+}
+
+static int ulpi_null_phy_post_resume(struct tegra_usb_phy *phy)
+{
+       DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
+       ulpi_null_phy_obs_read();
        return 0;
 }
 
@@ -2643,13 +2962,12 @@ static int ulpi_null_phy_resume(struct tegra_usb_phy *phy)
                writel(val, base + ULPI_TIMING_CTRL_0);
 
                /* enable ULPI pinmux bypass */
-               val = readl(base + ULPI_TIMING_CTRL_0);
-               val |= ULPI_OUTPUT_PINMUX_BYP;
-               writel(val, base + ULPI_TIMING_CTRL_0);
+               ulpi_pinmux_bypass(phy, true);
                udelay(5);
 #ifndef CONFIG_ARCH_TEGRA_2x_SOC
                /* remove DIR tristate */
-               tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DIR, TEGRA_TRI_NORMAL);
+               tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DIR,
+                                         TEGRA_TRI_NORMAL);
 #endif
        }
        return 0;
@@ -2667,10 +2985,12 @@ static struct tegra_usb_phy_ops utmi_phy_ops = {
        .resume = utmi_phy_resume,
        .post_resume    = utmi_phy_post_resume,
        .charger_detect = utmi_phy_charger_detect,
+       .post_suspend   = phy_post_suspend,
 };
 
 static struct tegra_usb_phy_ops uhsic_phy_ops = {
        .open           = uhsic_phy_open,
+       .close          = uhsic_phy_close,
        .irq            = uhsic_phy_irq,
        .power_on       = uhsic_phy_power_on,
        .power_off      = uhsic_phy_power_off,
@@ -2679,16 +2999,22 @@ static struct tegra_usb_phy_ops uhsic_phy_ops = {
        .post_resume = uhsic_phy_post_resume,
        .port_power = uhsic_phy_bus_port_power,
        .bus_reset      = uhsic_phy_bus_reset,
+       .post_suspend   = phy_post_suspend,
 };
 
 static struct tegra_usb_phy_ops ulpi_null_phy_ops = {
+       .open           = ulpi_null_phy_open,
+       .close          = ulpi_null_phy_close,
        .init           = ulpi_null_phy_init,
        .irq            = ulpi_null_phy_irq,
        .power_on       = ulpi_null_phy_power_on,
        .power_off      = ulpi_null_phy_power_off,
        .pre_resume = ulpi_null_phy_pre_resume,
        .resume = ulpi_null_phy_resume,
+       .post_resume = ulpi_null_phy_post_resume,
        .reset          = ulpi_null_phy_cmd_reset,
+       .post_suspend   = phy_post_suspend,
+       .bus_reset      = ulpi_phy_bus_reset,
 };
 
 static struct tegra_usb_phy_ops ulpi_link_phy_ops;