From 7b1ddc1c75665e5c4bf83ceccf4a8ff646c610ca Mon Sep 17 00:00:00 2001 From: Xin Xie Date: Tue, 10 Jul 2012 19:23:03 -0700 Subject: usb: otg: tegra: fix racing USB connection events Sometimes USB connect and disconnect events is not detected properly. The OTG irq handler is using a workqueue which has no protection for multiple incoming event handling. This patch adds mutex to protect the workqueue for USB connection handling. BUG 968345 Change-Id: If1c6fec4231dd0dc918f7f278e0a1d7667782917 Signed-off-by: Xin Xie Reviewed-on: http://git-master/r/122308 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Rakesh Bodla GVS: Gerrit_Virtual_Submit Reviewed-by: Venkat Moganty --- drivers/usb/otg/tegra-otg.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/usb/otg/tegra-otg.c b/drivers/usb/otg/tegra-otg.c index 6b55d5d5c6fb..9d614cb78570 100644 --- a/drivers/usb/otg/tegra-otg.c +++ b/drivers/usb/otg/tegra-otg.c @@ -53,6 +53,7 @@ struct tegra_otg_data { struct otg_transceiver otg; unsigned long int_status; spinlock_t lock; + struct mutex irq_work_mutex; void __iomem *regs; struct clk *clk; int irq; @@ -221,12 +222,15 @@ static void irq_work(struct work_struct *work) struct tegra_otg_data *tegra = container_of(work, struct tegra_otg_data, work); struct otg_transceiver *otg = &tegra->otg; - enum usb_otg_state from = otg->state; + enum usb_otg_state from; enum usb_otg_state to = OTG_STATE_UNDEFINED; unsigned long flags; unsigned long status; + mutex_lock(&tegra->irq_work_mutex); + spin_lock_irqsave(&tegra->lock, flags); + from = otg->state; status = tegra->int_status; /* Debug prints */ @@ -250,6 +254,7 @@ static void irq_work(struct work_struct *work) spin_unlock_irqrestore(&tegra->lock, flags); tegra_change_otg_state(tegra, to); + mutex_unlock(&tegra->irq_work_mutex); } static irqreturn_t tegra_otg_irq(int irq, void *data) @@ -395,6 +400,7 @@ static int tegra_otg_probe(struct platform_device *pdev) tegra->otg.set_suspend = tegra_otg_set_suspend; tegra->otg.set_power = tegra_otg_set_power; spin_lock_init(&tegra->lock); + mutex_init(&tegra->irq_work_mutex); if (pdata) { tegra->builtin_host = !pdata->ehci_pdata->builtin_host_disabled; @@ -498,6 +504,7 @@ static int __exit tegra_otg_remove(struct platform_device *pdev) clk_disable(tegra->clk); clk_put(tegra->clk); platform_set_drvdata(pdev, NULL); + mutex_destroy(&tegra->irq_work_mutex); kfree(tegra); return 0; @@ -510,6 +517,8 @@ static int tegra_otg_suspend(struct device *dev) struct tegra_otg_data *tegra = platform_get_drvdata(pdev); struct otg_transceiver *otg = &tegra->otg; int val; + + mutex_lock(&tegra->irq_work_mutex); DBG("%s(%d) BEGIN state : %s\n", __func__, __LINE__, tegra_state_name(otg->state)); @@ -526,6 +535,7 @@ static int tegra_otg_suspend(struct device *dev) tegra->suspended = true; DBG("%s(%d) END\n", __func__, __LINE__); + mutex_unlock(&tegra->irq_work_mutex); return 0; } @@ -537,8 +547,11 @@ static void tegra_otg_resume(struct device *dev) unsigned long flags; DBG("%s(%d) BEGIN\n", __func__, __LINE__); - if (!tegra->suspended) + mutex_lock(&tegra->irq_work_mutex); + if (!tegra->suspended) { + mutex_unlock(&tegra->irq_work_mutex); return; + } /* Clear pending interrupts */ clk_enable(tegra->clk); @@ -557,12 +570,12 @@ static void tegra_otg_resume(struct device *dev) spin_unlock_irqrestore(&tegra->lock, flags); schedule_work(&tegra->work); - enable_interrupt(tegra, true); tegra->suspended = false; DBG("%s(%d) END\n", __func__, __LINE__); + mutex_unlock(&tegra->irq_work_mutex); } static const struct dev_pm_ops tegra_otg_pm_ops = { -- cgit v1.2.3