summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorXin Xie <xxie@nvidia.com>2012-07-10 19:23:03 -0700
committerSimone Willett <swillett@nvidia.com>2012-08-17 12:20:18 -0700
commit7b1ddc1c75665e5c4bf83ceccf4a8ff646c610ca (patch)
tree730c02c817e4726ed180326d34560c8bd0488b71 /drivers
parent633c85bd905ef672e5e406c595b97deffd84a92a (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')
-rw-r--r--drivers/usb/otg/tegra-otg.c19
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 = {