summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorPrashant Gaikwad <pgaikwad@nvidia.com>2011-03-29 19:10:04 +0530
committerVarun Colbert <vcolbert@nvidia.com>2011-04-01 19:29:40 -0700
commit8014ff69b2b8a860840f04d26ad164ae8353b04d (patch)
treede40ee36f6375477636eae5f032d5b3ab4c5b152 /drivers
parentb1b863de1b1a83f8f9edd8e8e6172d3aa55f3f60 (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 Change-Id: I73fb92534ce5ae8d1ea39dfe756f940b89af7d79 Reviewed-on: http://git-master/r/24684 Reviewed-by: Varun Colbert <vcolbert@nvidia.com> Tested-by: Varun Colbert <vcolbert@nvidia.com>
Diffstat (limited to 'drivers')
-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;
}