diff options
author | Xin Xie <xxie@nvidia.com> | 2012-07-10 19:23:03 -0700 |
---|---|---|
committer | Simone Willett <swillett@nvidia.com> | 2012-08-17 12:20:18 -0700 |
commit | 7b1ddc1c75665e5c4bf83ceccf4a8ff646c610ca (patch) | |
tree | 730c02c817e4726ed180326d34560c8bd0488b71 /drivers/usb | |
parent | 633c85bd905ef672e5e406c595b97deffd84a92a (diff) |
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 <xxie@nvidia.com>
Reviewed-on: http://git-master/r/122308
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Rakesh Bodla <rbodla@nvidia.com>
GVS: Gerrit_Virtual_Submit
Reviewed-by: Venkat Moganty <vmoganty@nvidia.com>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/otg/tegra-otg.c | 19 |
1 files 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 = { |