]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - arch/arm/mach-tegra/usb_phy.c
Merge commit 'main-jb-2012.08.03-B4' into t114-0806
[linux-2.6.git] / arch / arm / mach-tegra / usb_phy.c
index d24ba12f74ef0c0d71dd35fedb06c8f962a454f8..c3060b7734bca2ad28601b58358c71144f8956b3 100644 (file)
@@ -2,7 +2,7 @@
  * arch/arm/mach-tegra/usb_phy.c
  *
  * Copyright (C) 2010 Google, Inc.
- * Copyright (C) 2010 - 2012 NVIDIA Corporation
+ * Copyright (c) 2010-2012, NVIDIA CORPORATION.  All rights reserved.
  *
  * Author:
  *     Erik Gilling <konkers@google.com>
 
 #include <mach/iomap.h>
 #include <mach/gpio-tegra.h>
+#include <mach/hardware.h>
 
 #include "tegra_usb_phy.h"
+#include "fuse.h"
 
 #define ERR(stuff...)          pr_err("usb_phy: " stuff)
 #define WARNING(stuff...)      pr_warning("usb_phy: " stuff)
@@ -119,14 +121,14 @@ int usb_phy_reg_status_wait(void __iomem *reg, u32 mask,
        return -1;
 }
 
-int tegra_usb_phy_init_ops(struct tegra_usb_phy *phy)
+static int tegra_usb_phy_init_ops(struct tegra_usb_phy *phy)
 {
        int err = 0;
 
        DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
 
        if (phy->pdata->has_hostpc)
-#if CONFIG_ARCH_TEGRA_11x_SOC
+#if defined(CONFIG_ARCH_TEGRA_11x_SOC)
                err = tegra11x_usb_phy_init_ops(phy);
 #else
                err = tegra3_usb_phy_init_ops(phy);
@@ -141,6 +143,16 @@ static irqreturn_t usb_phy_dev_vbus_pmu_irq_thr(int irq, void *pdata)
 {
        struct tegra_usb_phy *phy = pdata;
 
+       if (phy->vdd_reg && !phy->vdd_reg_on) {
+               regulator_enable(phy->vdd_reg);
+               phy->vdd_reg_on = true;
+               /*
+                * Optimal time to get the regulator turned on
+                * before detecting vbus interrupt.
+                */
+               mdelay(15);
+       }
+
        /* clk is disabled during phy power off and not here*/
        if (!phy->ctrl_clk_on) {
                clk_enable(phy->ctrlr_clk);
@@ -220,7 +232,6 @@ fail_ctrlr_clk:
        clk_put(phy->pllu_clk);
 
 fail_pll:
-
        return err;
 }
 
@@ -230,29 +241,34 @@ struct tegra_usb_phy *tegra_usb_phy_open(struct platform_device *pdev)
        struct tegra_usb_platform_data *pdata;
        struct resource *res;
        int err;
+       int plat_data_size = sizeof(struct tegra_usb_platform_data);
 
        DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, pdev->id);
        pdata = dev_get_platdata(&pdev->dev);
        if (!pdata) {
                dev_err(&pdev->dev, "inst:[%d] Platform data missing\n",
                                                                pdev->id);
-               return ERR_PTR(-EINVAL);
+               err = -EINVAL;
+               goto fail_inval;
        }
 
-       phy = kzalloc(sizeof(struct tegra_usb_phy), GFP_KERNEL);
+       phy = devm_kzalloc(&pdev->dev, sizeof(struct tegra_usb_phy), GFP_KERNEL);
        if (!phy) {
                ERR("inst:[%d] malloc usb phy failed\n", pdev->id);
-               return ERR_PTR(-ENOMEM);
+               err = -ENOMEM;
+               goto fail_nomem;
        }
 
-       phy->pdata = kzalloc(sizeof(struct tegra_usb_platform_data), GFP_KERNEL);
+       phy->pdata = devm_kzalloc(&pdev->dev, plat_data_size, GFP_KERNEL);
        if (!phy->pdata) {
                ERR("inst:[%d] malloc usb phy pdata failed\n", pdev->id);
-               kfree(phy);
-               return ERR_PTR(-ENOMEM);
+               devm_kfree(&pdev->dev, phy);
+               err = -ENOMEM;
+               goto fail_nomem;
        }
 
-       memcpy(phy->pdata, pdata, sizeof(struct tegra_usb_platform_data));
+       memcpy(phy->pdata, pdata, plat_data_size);
+
        phy->pdev = pdev;
        phy->inst = pdev->id;
 
@@ -272,11 +288,13 @@ struct tegra_usb_phy *tegra_usb_phy_open(struct platform_device *pdev)
                goto fail_io;
        }
 
-       phy->vdd_reg = regulator_get(NULL, "avdd_usb");
+       phy->vdd_reg = regulator_get(&pdev->dev, "avdd_usb");
        if (IS_ERR_OR_NULL(phy->vdd_reg)) {
                ERR("inst:[%d] couldn't get regulator avdd_usb: %ld\n",
                        phy->inst, PTR_ERR(phy->vdd_reg));
                phy->vdd_reg = NULL;
+               err = PTR_ERR(phy->vdd_reg);
+               goto fail_io;
        }
 
        err = tegra_usb_phy_get_clocks(phy);
@@ -318,8 +336,6 @@ struct tegra_usb_phy *tegra_usb_phy_open(struct platform_device *pdev)
                                                 req failed\n", phy->inst);
                                        goto fail_init;
                                }
-                               if (gpio < TEGRA_NR_GPIOS)
-                                       tegra_gpio_enable(gpio);
                                if (gpio_direction_output(gpio, 1) < 0) {
                                        ERR("inst:[%d] host vbus gpio \
                                                 dir failed\n", phy->inst);
@@ -347,9 +363,6 @@ struct tegra_usb_phy *tegra_usb_phy_open(struct platform_device *pdev)
                }
        }
 
-       if (phy->vdd_reg)
-               regulator_enable(phy->vdd_reg);
-
        return phy;
 
 fail_init:
@@ -376,8 +389,11 @@ fail_clk:
        regulator_put(phy->vdd_reg);
        iounmap(phy->regs);
 fail_io:
-       kfree(phy);
+       devm_kfree(&pdev->dev, phy->pdata);
+       devm_kfree(&pdev->dev, phy);
 
+fail_nomem:
+fail_inval:
        return ERR_PTR(err);
 }
 EXPORT_SYMBOL_GPL(tegra_usb_phy_open);
@@ -412,15 +428,15 @@ void tegra_usb_phy_close(struct tegra_usb_phy *phy)
        }
 
        if (phy->vdd_reg) {
-               regulator_disable(phy->vdd_reg);
+               if (phy->vdd_reg_on)
+                       regulator_disable(phy->vdd_reg);
                regulator_put(phy->vdd_reg);
        }
 
-
        tegra_usb_phy_release_clocks(phy);
 
-       kfree(phy->pdata);
-       kfree(phy);
+       devm_kfree(&phy->pdev->dev, phy->pdata);
+       devm_kfree(&phy->pdev->dev, phy);
 }
 EXPORT_SYMBOL_GPL(tegra_usb_phy_close);
 
@@ -433,6 +449,8 @@ irqreturn_t tegra_usb_phy_irq(struct tegra_usb_phy *phy)
 
        return status;
 }
+EXPORT_SYMBOL_GPL(tegra_usb_phy_irq);
+
 int tegra_usb_phy_init(struct tegra_usb_phy *phy)
 {
        int status = 0;
@@ -447,6 +465,7 @@ int tegra_usb_phy_init(struct tegra_usb_phy *phy)
 
        return status;
 }
+EXPORT_SYMBOL_GPL(tegra_usb_phy_init);
 
 int tegra_usb_phy_power_off(struct tegra_usb_phy *phy)
 {
@@ -469,15 +488,27 @@ int tegra_usb_phy_power_off(struct tegra_usb_phy *phy)
        clk_disable(phy->sys_clk);
        if (phy->pdata->op_mode == TEGRA_USB_OPMODE_HOST) {
                if (!phy->pdata->u_data.host.hot_plug &&
-                       !phy->pdata->u_data.host.remote_wakeup_supported)
+                       !phy->pdata->u_data.host.remote_wakeup_supported) {
                        clk_disable(phy->ctrlr_clk);
+                       phy->ctrl_clk_on = false;
+                       if (phy->vdd_reg && phy->vdd_reg_on) {
+                               regulator_disable(phy->vdd_reg);
+                               phy->vdd_reg_on = false;
+                       }
+               }
        } else {
-               /* In device mode clock is turned on by pmu irq handler
-                * if pmu irq is not available clocks will not be turned off/on
+               /* In device mode clock regulator/clocks will be turned off
+                * only if pmu interrupt is present on the board and host mode
+                * support through OTG is supported on the board.
                 */
-               if (phy->pdata->u_data.dev.vbus_pmu_irq) {
+               if (phy->pdata->u_data.dev.vbus_pmu_irq &&
+                       phy->pdata->builtin_host_disabled) {
                        clk_disable(phy->ctrlr_clk);
                        phy->ctrl_clk_on = false;
+                       if (phy->vdd_reg && phy->vdd_reg_on) {
+                               regulator_disable(phy->vdd_reg);
+                               phy->vdd_reg_on = false;
+                       }
                }
        }
 
@@ -485,6 +516,7 @@ int tegra_usb_phy_power_off(struct tegra_usb_phy *phy)
 
        return err;
 }
+EXPORT_SYMBOL_GPL(tegra_usb_phy_power_off);
 
 int tegra_usb_phy_power_on(struct tegra_usb_phy *phy)
 {
@@ -495,6 +527,13 @@ int tegra_usb_phy_power_on(struct tegra_usb_phy *phy)
        if (phy->phy_power_on)
                return status;
 
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+       if (phy->vdd_reg && !phy->vdd_reg_on) {
+               regulator_enable(phy->vdd_reg);
+               phy->vdd_reg_on = true;
+       }
+#endif
+
        /* In device mode clock is turned on by pmu irq handler
         * if pmu irq is not available clocks will not be turned off/on
         */
@@ -536,6 +575,8 @@ int tegra_usb_phy_reset(struct tegra_usb_phy *phy)
 
        return status;
 }
+EXPORT_SYMBOL_GPL(tegra_usb_phy_reset);
+
 int tegra_usb_phy_pre_suspend(struct tegra_usb_phy *phy)
 {
        int status = 0;
@@ -550,6 +591,7 @@ int tegra_usb_phy_pre_suspend(struct tegra_usb_phy *phy)
 
        return status;
 }
+
 int tegra_usb_phy_suspend(struct tegra_usb_phy *phy)
 {
        int err = 0;
@@ -565,6 +607,8 @@ int tegra_usb_phy_suspend(struct tegra_usb_phy *phy)
 
        return err;
 }
+EXPORT_SYMBOL_GPL(tegra_usb_phy_suspend);
+
 int tegra_usb_phy_post_suspend(struct tegra_usb_phy *phy)
 {
        int status = 0;
@@ -579,6 +623,8 @@ int tegra_usb_phy_post_suspend(struct tegra_usb_phy *phy)
 
        return status;
 }
+EXPORT_SYMBOL_GPL(tegra_usb_phy_post_suspend);
+
 int tegra_usb_phy_pre_resume(struct tegra_usb_phy *phy, bool remote_wakeup)
 {
        int status = 0;
@@ -593,6 +639,8 @@ int tegra_usb_phy_pre_resume(struct tegra_usb_phy *phy, bool remote_wakeup)
 
        return status;
 }
+EXPORT_SYMBOL_GPL(tegra_usb_phy_pre_resume);
+
 int tegra_usb_phy_resume(struct tegra_usb_phy *phy)
 {
        int err = 0;
@@ -609,6 +657,8 @@ int tegra_usb_phy_resume(struct tegra_usb_phy *phy)
        return err;
 
 }
+EXPORT_SYMBOL_GPL(tegra_usb_phy_resume);
+
 int tegra_usb_phy_post_resume(struct tegra_usb_phy *phy)
 {
        int status = 0;
@@ -623,6 +673,8 @@ int tegra_usb_phy_post_resume(struct tegra_usb_phy *phy)
 
        return status;
 }
+EXPORT_SYMBOL_GPL(tegra_usb_phy_post_resume);
+
 int tegra_usb_phy_port_power(struct tegra_usb_phy *phy)
 {
        int status = 0;
@@ -632,11 +684,10 @@ int tegra_usb_phy_port_power(struct tegra_usb_phy *phy)
        if (phy->ops && phy->ops->port_power)
                status = phy->ops->port_power(phy);
 
-       if (phy->pdata->ops && phy->pdata->ops->port_power)
-               phy->pdata->ops->port_power();
-
        return status;
 }
+EXPORT_SYMBOL_GPL(tegra_usb_phy_port_power);
+
 int tegra_usb_phy_bus_reset(struct tegra_usb_phy *phy)
 {
        int status = 0;
@@ -648,6 +699,7 @@ int tegra_usb_phy_bus_reset(struct tegra_usb_phy *phy)
 
        return status;
 }
+EXPORT_SYMBOL_GPL(tegra_usb_phy_bus_reset);
 
 bool tegra_usb_phy_charger_detected(struct tegra_usb_phy *phy)
 {
@@ -668,21 +720,25 @@ bool tegra_usb_phy_hw_accessible(struct tegra_usb_phy *phy)
 
        return phy->hw_accessible;
 }
+EXPORT_SYMBOL_GPL(tegra_usb_phy_hw_accessible);
 
 bool tegra_usb_phy_remote_wakeup(struct tegra_usb_phy *phy)
 {
        return phy->remote_wakeup;
 }
+EXPORT_SYMBOL_GPL(tegra_usb_phy_remote_wakeup);
 
 bool tegra_usb_phy_has_hostpc(struct tegra_usb_phy *phy)
 {
        return phy->pdata->has_hostpc;
 }
+EXPORT_SYMBOL_GPL(tegra_usb_phy_has_hostpc);
 
 bool tegra_usb_phy_otg_supported(struct tegra_usb_phy *phy)
 {
        return phy->pdata->port_otg;
 }
+EXPORT_SYMBOL_GPL(tegra_usb_phy_otg_supported);
 
 void tegra_usb_phy_memory_prefetch_on(struct tegra_usb_phy *phy)
 {
@@ -713,3 +769,4 @@ void tegra_usb_phy_memory_prefetch_off(struct tegra_usb_phy *phy)
                writel(val, ahb_gizmo + AHB_MEM_PREFETCH_CFG2);
        }
 }
+