CHROMIUM: exynos: properly manage "regulator" for HSIC reset
authorVincent Palatin <vpalatin@chromium.org>
Tue, 5 Feb 2013 17:12:19 +0000 (09:12 -0800)
committerChromeBot <chrome-bot@google.com>
Tue, 5 Feb 2013 23:43:54 +0000 (15:43 -0800)
We were violating something in the documentation for regulator_put,
namely:
  Note: drivers must ensure that all regulator_enable calls made on
  this regulator source are balanced by regulator_disable calls prior
  to calling this function.

We now keep the regulator around until we've disabled it.  That keeps
the regulator in the right state.

At the same time, add some error checking to regulator calls.

BUG=chromium-os:38403
TEST=on Snow with 3G modem, do a suspend/resume cycle and check in
"lsusb" that the modem (VID/PID 1410:a021) and the camera (VID/PID
2232:1037) are still there after resume.
On Spring, check we can still boot from USB.
TEST=Check state of hsichub-reset-l at boot and after resume:
1. cd /sys/kernel/debug/regulator/hsichub-reset-l
2. grep "" *
3. Check open_count of 1 and use_count of 1.

Change-Id: I31012c15e30c43a1259f15ffd3592ee68cb0b772
Signed-off-by: Doug Anderson <dianders@chromium.org>
Signed-off-by: Vincent Palatin <vpalatin@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/42621
Reviewed-by: Todd Broch <tbroch@chromium.org>
arch/arm/mach-exynos/setup-usb-phy.c

index b3d32ee..02c92f7 100644 (file)
@@ -49,6 +49,8 @@ static struct exynos_usb_phy usb_phy_control;
 static DEFINE_SPINLOCK(phy_lock);
 static bool ext_clk;
 
+static struct regulator *hub_reset;
+
 static int exynos4_usb_host_phy_is_on(void)
 {
        return (readl(EXYNOS4_PHYPWR) & PHY1_STD_ANALOG_POWERDOWN) ? 0 : 1;
@@ -347,7 +349,7 @@ static int exynos5_usb_phy20_init(struct platform_device *pdev)
        u32 refclk_freq;
        u32 hostphy_ctrl0, otgphy_sys, hsic_ctrl, ehcictrl;
        struct clk *host_clk = NULL;
-       struct regulator *hub_reset;
+       int ret;
 
        atomic_inc(&host_usage);
 
@@ -404,16 +406,20 @@ static int exynos5_usb_phy20_init(struct platform_device *pdev)
        writel(hostphy_ctrl0, EXYNOS5_PHY_HOST_CTRL0);
 
        /* HSIC phy reset */
+       WARN_ON(hub_reset != NULL);
        hub_reset = regulator_get(NULL, "hsichub-reset-l");
        if (!IS_ERR(hub_reset)) {
-               /*
-                * toggle the reset line of the HSIC hub chip.
-                */
-               regulator_force_disable(hub_reset);
+               /* toggle the reset line of the HSIC hub chip. */
+
+               /* Ought to be disabled but force line just in case */
+               ret = regulator_force_disable(hub_reset);
+               WARN_ON(ret);
+
                /* keep reset active during 100 us */
                udelay(100);
-               regulator_enable(hub_reset);
-               regulator_put(hub_reset);
+               ret = regulator_enable(hub_reset);
+               WARN_ON(ret);
+
                /* Hub init phase takes up to 4 ms */
                usleep_range(4000, 10000);
        }
@@ -443,6 +449,7 @@ static int exynos5_usb_phy20_exit(struct platform_device *pdev)
 {
        u32 hostphy_ctrl0, otgphy_sys, hsic_ctrl;
        struct clk *host_clk = NULL;
+       int ret;
 
        if (atomic_dec_return(&host_usage) > 0) {
                dev_info(&pdev->dev, "still being used\n");
@@ -476,6 +483,15 @@ static int exynos5_usb_phy20_exit(struct platform_device *pdev)
 
        exynos_usb_phy_clock_disable(host_clk);
 
+       /* put HSIC under reset */
+       WARN_ON(hub_reset == NULL);
+       if (!IS_ERR_OR_NULL(hub_reset)) {
+               ret = regulator_disable(hub_reset);
+               WARN_ON(ret);
+               regulator_put(hub_reset);
+               hub_reset = NULL;
+       }
+
        return 0;
 }