summaryrefslogtreecommitdiff
path: root/drivers/usb/otg
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/otg')
-rw-r--r--drivers/usb/otg/tegra-otg.c54
1 files changed, 52 insertions, 2 deletions
diff --git a/drivers/usb/otg/tegra-otg.c b/drivers/usb/otg/tegra-otg.c
index 5e19542385c2..cdb4cfbfb4f7 100644
--- a/drivers/usb/otg/tegra-otg.c
+++ b/drivers/usb/otg/tegra-otg.c
@@ -54,8 +54,12 @@ struct tegra_otg_data {
struct platform_device *pdev;
struct work_struct work;
unsigned int intr_reg_data;
+ bool detect_vbus;
+ bool clk_enabled;
};
+static struct tegra_otg_data *tegra_clone;
+
static inline unsigned long otg_readl(struct tegra_otg_data *tegra,
unsigned int offset)
{
@@ -68,6 +72,20 @@ static inline void otg_writel(struct tegra_otg_data *tegra, unsigned long val,
writel(val, tegra->regs + offset);
}
+static void tegra_otg_enable_clk(void)
+{
+ if (!tegra_clone->clk_enabled)
+ clk_enable(tegra_clone->clk);
+ tegra_clone->clk_enabled = true;
+}
+
+static void tegra_otg_disable_clk(void)
+{
+ if (tegra_clone->clk_enabled)
+ clk_disable(tegra_clone->clk);
+ tegra_clone->clk_enabled = false;
+}
+
static const char *tegra_state_name(enum usb_otg_state state)
{
if (state == OTG_STATE_A_HOST)
@@ -106,6 +124,12 @@ static void irq_work(struct work_struct *work)
unsigned long flags;
unsigned long status;
+ if (tegra->detect_vbus) {
+ tegra->detect_vbus = false;
+ tegra_otg_enable_clk();
+ return;
+ }
+
clk_enable(tegra->clk);
spin_lock_irqsave(&tegra->lock, flags);
@@ -152,7 +176,8 @@ static void irq_work(struct work_struct *work)
}
}
clk_disable(tegra->clk);
-
+ if (from != to)
+ tegra_otg_disable_clk();
}
static irqreturn_t tegra_otg_irq(int irq, void *data)
@@ -168,6 +193,7 @@ static irqreturn_t tegra_otg_irq(int irq, void *data)
if ((val & USB_ID_INT_STATUS) || (val & USB_VBUS_INT_STATUS)) {
tegra->int_status = val;
+ tegra->detect_vbus = false;
schedule_work(&tegra->work);
}
@@ -176,6 +202,13 @@ static irqreturn_t tegra_otg_irq(int irq, void *data)
return IRQ_HANDLED;
}
+void tegra_otg_check_vbus_detection(void)
+{
+ tegra_clone->detect_vbus = true;
+ schedule_work(&tegra_clone->work);
+}
+EXPORT_SYMBOL(tegra_otg_check_vbus_detection);
+
static int tegra_otg_set_peripheral(struct otg_transceiver *otg,
struct usb_gadget *gadget)
{
@@ -202,6 +235,7 @@ static int tegra_otg_set_peripheral(struct otg_transceiver *otg,
if ((val & USB_ID_INT_STATUS) || (val & USB_VBUS_INT_STATUS)) {
tegra->int_status = val;
+ tegra->detect_vbus = false;
schedule_work (&tegra->work);
}
@@ -258,6 +292,8 @@ static int tegra_otg_probe(struct platform_device *pdev)
spin_lock_init(&tegra->lock);
platform_set_drvdata(pdev, tegra);
+ tegra_clone = tegra;
+ tegra->clk_enabled = false;
tegra->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(tegra->clk)) {
@@ -343,10 +379,20 @@ static int __exit tegra_otg_remove(struct platform_device *pdev)
static int tegra_otg_suspend(struct platform_device *pdev, pm_message_t state)
{
struct tegra_otg_data *tegra_otg = platform_get_drvdata(pdev);
-
+ struct otg_transceiver *otg = &tegra_otg->otg;
+ enum usb_otg_state from = otg->state;
/* store the interupt enable for cable ID and VBUS */
+ clk_enable(tegra_otg->clk);
tegra_otg->intr_reg_data = readl(tegra_otg->regs + USB_PHY_WAKEUP);
+ clk_disable(tegra_otg->clk);
+ if (from == OTG_STATE_A_HOST)
+ tegra_stop_host(tegra_otg);
+ else if (from == OTG_STATE_B_PERIPHERAL && otg->gadget)
+ usb_gadget_vbus_disconnect(otg->gadget);
+
+ otg->state = OTG_STATE_A_SUSPEND;
+ tegra_otg_disable_clk();
return 0;
}
@@ -354,8 +400,12 @@ static int tegra_otg_resume(struct platform_device * pdev)
{
struct tegra_otg_data *tegra_otg = platform_get_drvdata(pdev);
+ tegra_otg_enable_clk();
+
/* restore the interupt enable for cable ID and VBUS */
+ clk_enable(tegra_otg->clk);
writel(tegra_otg->intr_reg_data, (tegra_otg->regs + USB_PHY_WAKEUP));
+ clk_disable(tegra_otg->clk);
return 0;
}