diff options
Diffstat (limited to 'drivers/usb/otg')
-rw-r--r-- | drivers/usb/otg/tegra-otg.c | 54 |
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; } |