diff options
author | Prashant Gaikwad <pgaikwad@nvidia.com> | 2011-03-29 19:10:04 +0530 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2011-11-30 21:43:27 -0800 |
commit | 6140af31a4a7ffb0b189bb32f87f81b73fd1d947 (patch) | |
tree | 6960bcbcaa90687a56ec6a0a84fd1c5df4e61a0d /drivers/usb/otg | |
parent | 20c6af1a73297a66a16daa3b70318097bb17476c (diff) |
tegra: otg: detect usb hotplug
Detect vbus and enable clock for usb otg using the interrupt received in
usb phy. usb phy interrupt is configured to max8907c usb detect interrupt to
get event for usb insertion when otg is suspended.
Bug 806949
Original-Change-Id: I73fb92534ce5ae8d1ea39dfe756f940b89af7d79
Reviewed-on: http://git-master/r/24684
Reviewed-by: Varun Colbert <vcolbert@nvidia.com>
Tested-by: Varun Colbert <vcolbert@nvidia.com>
Rebase-Id: Ra98c94151d4ce175090ee864008c17376126bf3c
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; } |