OSDN Git Service

usb: phy: qusb: Set clamp_dig_n signal based on usb status
authorVamsi Krishna Samavedam <vskrishn@codeaurora.org>
Tue, 9 Aug 2016 00:53:48 +0000 (17:53 -0700)
committerVamsi Krishna Samavedam <vskrishn@codeaurora.org>
Tue, 30 Aug 2016 19:22:35 +0000 (12:22 -0700)
Analog and digital power rails connected to the phy can be turned
on/off in any order. This may result in random leakage in the phy
as it expects certain power rails to be on/off in certain order.
Avoid random leakage on qusb2 phy by
1. Disable pll when phy is suspended/disconnected.
2. Reset and assert clamp dig_n signal to put dp/dm lines in high
   impedance state.

Change-Id: I1bafa7f824af8bbb3f67a71b81bf23b0a9c7164e
Signed-off-by: Vamsi Krishna Samavedam <vskrishn@codeaurora.org>
Documentation/devicetree/bindings/usb/msm-phy.txt
drivers/usb/phy/phy-msm-qusb-v2.c

index dd9c13b..bcb866a 100644 (file)
@@ -185,6 +185,9 @@ Optional properties:
    via the QSCRATCH interface.
    "emu_phy_base" : phy base address used for programming emulation target phy.
    "ref_clk_addr" : ref_clk bcr address used for on/off ref_clk before reset.
+   "tcsr_clamp_dig_n" : To enable/disable digital clamp to the phy. When
+   de-asserted, it will prevent random leakage from qusb2 phy resulting from
+   out of sequence turn on/off of 1p8, 3p3 and DVDD regulators.
  - clocks: a list of phandles to the PHY clocks. Use as per
    Documentation/devicetree/bindings/clock/clock-bindings.txt
  - clock-names: Names of the clocks in 1-1 correspondence with the "clocks"
index 23a4ac1..6b2e33e 100644 (file)
@@ -65,6 +65,8 @@
 #define LINESTATE_DP                   BIT(0)
 #define LINESTATE_DM                   BIT(1)
 
+#define QUSB2PHY_PLL_ANALOG_CONTROLS_ONE       0x0
+
 unsigned int phy_tune2;
 module_param(phy_tune2, uint, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(phy_tune2, "QUSB PHY v2 TUNE2");
@@ -73,6 +75,7 @@ struct qusb_phy {
        struct usb_phy          phy;
        void __iomem            *base;
        void __iomem            *tune2_efuse_reg;
+       void __iomem            *tcsr_clamp_dig_n;
 
        struct clk              *ref_clk_src;
        struct clk              *ref_clk;
@@ -554,6 +557,11 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend)
                /* Bus suspend case */
                if (qphy->cable_connected ||
                        (qphy->phy.flags & PHY_HOST_MODE)) {
+
+                       /* enable clock bypass */
+                       writel_relaxed(0x90,
+                               qphy->base + QUSB2PHY_PLL_ANALOG_CONTROLS_ONE);
+
                        /* Disable all interrupts */
                        writel_relaxed(0x00,
                                qphy->base + QUSB2PHY_INTR_CTRL);
@@ -584,15 +592,20 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend)
                        wmb();
                        qusb_phy_enable_clocks(qphy, false);
                } else { /* Cable disconnect case */
-                       /* Disable all interrupts */
-                       writel_relaxed(0x00,
-                               qphy->base + QUSB2PHY_INTR_CTRL);
 
-                       /* Put PHY into non-driving mode */
-                       writel_relaxed(0x23,
-                               qphy->base + QUSB2PHY_PWR_CTRL1);
+                       clk_reset(qphy->phy_reset, CLK_RESET_ASSERT);
+                       usleep_range(100, 150);
+                       clk_reset(qphy->phy_reset, CLK_RESET_DEASSERT);
 
-                       /* Makes sure that above write goes through */
+                       /* enable clock bypass */
+                       writel_relaxed(0x90,
+                               qphy->base + QUSB2PHY_PLL_ANALOG_CONTROLS_ONE);
+
+                       writel_relaxed(0x0, qphy->tcsr_clamp_dig_n);
+                       /*
+                        * clamp needs asserted before
+                        * power/clocks can be turned off
+                        */
                        wmb();
 
                        qusb_phy_enable_clocks(qphy, false);
@@ -604,6 +617,11 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend)
                if (qphy->cable_connected ||
                        (qphy->phy.flags & PHY_HOST_MODE)) {
                        qusb_phy_enable_clocks(qphy, true);
+
+                       /* disable clock bypass */
+                       writel_relaxed(0x80,
+                               qphy->base + QUSB2PHY_PLL_ANALOG_CONTROLS_ONE);
+
                        /* Clear all interrupts on resume */
                        writel_relaxed(0x00,
                                qphy->base + QUSB2PHY_INTR_CTRL);
@@ -611,6 +629,16 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend)
                        /* Makes sure that above write goes through */
                        wmb();
                } else { /* Cable connect case */
+                       writel_relaxed(0x1, qphy->tcsr_clamp_dig_n);
+
+                       /*
+                        * clamp needs de-asserted before
+                        * power/clocks can be turned on
+                        */
+                       wmb();
+
+                       clk_reset(qphy->phy_reset, CLK_RESET_DEASSERT);
+
                        qusb_phy_enable_power(qphy, true, true);
                        qusb_phy_enable_clocks(qphy, true);
                }
@@ -735,6 +763,16 @@ static int qusb_phy_probe(struct platform_device *pdev)
        }
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                               "tcsr_clamp_dig_n_1p8");
+       if (res) {
+               qphy->tcsr_clamp_dig_n = devm_ioremap_resource(dev, res);
+               if (IS_ERR(qphy->tcsr_clamp_dig_n)) {
+                       dev_dbg(dev, "couldn't ioremap tcsr_clamp_dig_n\n");
+                       return PTR_ERR(qphy->tcsr_clamp_dig_n);
+               }
+       }
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
                                                        "tune2_efuse_addr");
        if (res) {
                qphy->tune2_efuse_reg = devm_ioremap_nocache(dev, res->start,