]> rtime.felk.cvut.cz Git - sojka/nv-tegra/linux-3.10.git/commitdiff
usb: otg: tegra: add vbus control in otg driver
authorRakesh Bodla <rbodla@nvidia.com>
Wed, 12 Jun 2013 06:43:02 +0000 (12:13 +0530)
committerDan Willemsen <dwillemsen@nvidia.com>
Sat, 14 Sep 2013 20:28:22 +0000 (13:28 -0700)
If USB port has OTG support, allow the OTG driver
to control the vbus.

Bug 934024

Change-Id: Ib129ee2d1f60c18ae17afa146421cab0acca7141
Signed-off-by: Rakesh Bodla <rbodla@nvidia.com>
Reviewed-on: http://git-master/r/239341
(cherry picked from commit d15f126d9568ddbceb808078440c6030574212c3)
Reviewed-on: http://git-master/r/247151
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Venkat Moganty <vmoganty@nvidia.com>
drivers/usb/phy/tegra-otg.c

index 30af98968cb6fecaea616894f842f5bff2d01d29..2bc26e1abb8d38f0cea585741190c1f7b8b92e57 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/extcon.h>
 #include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
 
 #include <mach/pm_domains.h>
 
@@ -69,7 +70,9 @@ struct tegra_otg {
        struct clk *clk;
        int irq;
        struct work_struct work;
+       struct regulator *vbus_reg;
        unsigned int intr_reg_data;
+       bool turn_off_vbus_on_lp0;
        bool clk_enabled;
        bool interrupt_mode;
        bool suspended;
@@ -286,6 +289,35 @@ static void tegra_otg_notify_event(struct tegra_otg *tegra, int event)
        atomic_notifier_call_chain(&tegra->phy.notifier, event, tegra->phy.otg->gadget);
 }
 
+static int tegra_otg_start_host(struct tegra_otg *tegra, int on)
+{
+       if (on) {
+               regulator_enable(tegra->vbus_reg);
+               tegra_start_host(tegra);
+               tegra_otg_notify_event(tegra, USB_EVENT_ID);
+       } else {
+               tegra_stop_host(tegra);
+               tegra_otg_notify_event(tegra, USB_EVENT_NONE);
+               regulator_disable(tegra->vbus_reg);
+       }
+       return 0;
+}
+
+static int tegra_otg_start_gadget(struct tegra_otg *tegra, int on)
+{
+       struct usb_otg *otg = tegra->phy.otg;
+
+       if (on) {
+               usb_gadget_vbus_connect(otg->gadget);
+               tegra_otg_notify_event(tegra, USB_EVENT_VBUS);
+       } else {
+               usb_gadget_vbus_disconnect(otg->gadget);
+               tegra_otg_notify_event(tegra, USB_EVENT_NONE);
+       }
+
+       return 0;
+}
+
 static void tegra_change_otg_state(struct tegra_otg *tegra,
                                enum usb_otg_state to)
 {
@@ -306,22 +338,16 @@ static void tegra_change_otg_state(struct tegra_otg *tegra,
 
                if (from == OTG_STATE_A_SUSPEND) {
                        pm_runtime_get_sync(tegra->phy.dev);
-                       if (to == OTG_STATE_B_PERIPHERAL && otg->gadget) {
-                               usb_gadget_vbus_connect(otg->gadget);
-                               tegra_otg_notify_event(tegra, USB_EVENT_VBUS);
-                       }
-                       else if (to == OTG_STATE_A_HOST) {
-                               tegra_start_host(tegra);
-                               tegra_otg_notify_event(tegra, USB_EVENT_ID);
-                       }
+                       if (to == OTG_STATE_B_PERIPHERAL && otg->gadget)
+                               tegra_otg_start_gadget(tegra, 1);
+                       else if (to == OTG_STATE_A_HOST)
+                               tegra_otg_start_host(tegra, 1);
                } else if (from == OTG_STATE_A_HOST && to == OTG_STATE_A_SUSPEND) {
-                       tegra_stop_host(tegra);
-                       tegra_otg_notify_event(tegra, USB_EVENT_NONE);
+                       tegra_otg_start_host(tegra, 0);
                        pm_runtime_mark_last_busy(tegra->phy.dev);
                        pm_runtime_put_autosuspend(tegra->phy.dev);
                } else if (from == OTG_STATE_B_PERIPHERAL && otg->gadget && to == OTG_STATE_A_SUSPEND) {
-                       usb_gadget_vbus_disconnect(otg->gadget);
-                       tegra_otg_notify_event(tegra, USB_EVENT_NONE);
+                       tegra_otg_start_gadget(tegra, 0);
                        pm_runtime_mark_last_busy(tegra->phy.dev);
                        pm_runtime_put_autosuspend(tegra->phy.dev);
                }
@@ -546,6 +572,13 @@ static int tegra_otg_conf(struct platform_device *pdev)
                return -ENOMEM;
        }
 
+       tegra->vbus_reg = regulator_get(&pdev->dev, "usb_vbus");
+       if (IS_ERR_OR_NULL(tegra->vbus_reg)) {
+               pr_err("failed to get regulator usb_vbus: %ld\n",
+                       PTR_ERR(tegra->vbus_reg));
+               tegra->vbus_reg = NULL;
+       }
+
        spin_lock_init(&tegra->lock);
        mutex_init(&tegra->irq_work_mutex);
 
@@ -556,6 +589,8 @@ static int tegra_otg_conf(struct platform_device *pdev)
 
        tegra->interrupt_mode = true;
        tegra->suspended = false;
+       tegra->turn_off_vbus_on_lp0 =
+                       pdata->ehci_pdata->u_data.host.turn_off_vbus_on_lp0;
        tegra->support_pmu_vbus = pdata->ehci_pdata->support_pmu_vbus;
        tegra->id_det_gpio = pdata->id_det_gpio ? pdata->id_det_gpio : -1;
        tegra->pdata = pdata;
@@ -622,6 +657,8 @@ err_id_extcon:
                extcon_unregister_notifier(tegra->vbus_extcon_dev,
                                                &otg_vbus_nb);
 err_vbus_extcon:
+       if (tegra->vbus_reg)
+               regulator_put(tegra->vbus_reg);
        return err;
 }
 
@@ -755,6 +792,9 @@ static int __exit tegra_otg_remove(struct platform_device *pdev)
 {
        struct tegra_otg *tegra = platform_get_drvdata(pdev);
 
+       if (tegra->vbus_reg)
+               regulator_put(tegra->vbus_reg);
+
        if (tegra->support_gpio_id && gpio_is_valid(tegra->id_det_gpio)) {
                free_irq(gpio_to_irq(tegra->id_det_gpio), tegra);
                gpio_free(tegra->id_det_gpio);
@@ -781,13 +821,12 @@ static int tegra_otg_suspend(struct device *dev)
 {
        struct platform_device *pdev = to_platform_device(dev);
        struct tegra_otg *tegra = platform_get_drvdata(pdev);
-       struct usb_phy *phy = &tegra->phy;
-       enum usb_otg_state from = phy->otg->phy->state;
+       enum usb_otg_state from = tegra->phy.state;
        unsigned int val;
 
        mutex_lock(&tegra->irq_work_mutex);
        DBG("%s(%d) BEGIN state : %s\n", __func__, __LINE__,
-                               tegra_state_name(phy->otg->phy->state));
+                               tegra_state_name(tegra->phy->state));
 
        pm_runtime_get_sync(dev);
        clk_prepare_enable(tegra->clk);
@@ -801,8 +840,10 @@ static int tegra_otg_suspend(struct device *dev)
        if (from == OTG_STATE_B_PERIPHERAL)
                tegra_change_otg_state(tegra, OTG_STATE_A_SUSPEND);
 
-       tegra->suspended = true;
+       if (from == OTG_STATE_A_HOST && tegra->turn_off_vbus_on_lp0)
+               regulator_disable(tegra->vbus_reg);
 
+       tegra->suspended = true;
        DBG("%s(%d) END\n", __func__, __LINE__);
        mutex_unlock(&tegra->irq_work_mutex);
 
@@ -813,10 +854,13 @@ static void tegra_otg_resume(struct device *dev)
 {
        struct platform_device *pdev = to_platform_device(dev);
        struct tegra_otg *tegra = platform_get_drvdata(pdev);
+       enum usb_otg_state from = tegra->phy.state;
        int val;
        unsigned long flags;
 
        DBG("%s(%d) BEGIN\n", __func__, __LINE__);
+       if (from == OTG_STATE_A_HOST && tegra->turn_off_vbus_on_lp0)
+               regulator_enable(tegra->vbus_reg);
 
        mutex_lock(&tegra->irq_work_mutex);
        if (!tegra->suspended) {