|
Oliver Neukum |
b7e0f0 |
From 451b15ed138ec15bffbebb58a00ebdd884c3e659 Mon Sep 17 00:00:00 2001
|
|
Oliver Neukum |
b7e0f0 |
From: Xu Yang <xu.yang_2@nxp.com>
|
|
Oliver Neukum |
b7e0f0 |
Date: Fri, 17 Mar 2023 14:15:16 +0800
|
|
Oliver Neukum |
b7e0f0 |
Subject: [PATCH] usb: chipidea: core: fix possible concurrent when switch role
|
|
Oliver Neukum |
b7e0f0 |
Git-commit: 451b15ed138ec15bffbebb58a00ebdd884c3e659
|
|
Oliver Neukum |
b7e0f0 |
References: git-fixes
|
|
Oliver Neukum |
b7e0f0 |
Patch-mainline: v6.3-rc4
|
|
Oliver Neukum |
b7e0f0 |
|
|
Oliver Neukum |
b7e0f0 |
The user may call role_store() when driver is handling
|
|
Oliver Neukum |
b7e0f0 |
ci_handle_id_switch() which is triggerred by otg event or power lost
|
|
Oliver Neukum |
b7e0f0 |
event. Unfortunately, the controller may go into chaos in this case.
|
|
Oliver Neukum |
b7e0f0 |
Fix this by protecting it with mutex lock.
|
|
Oliver Neukum |
b7e0f0 |
|
|
Oliver Neukum |
b7e0f0 |
Fixes: a932a8041ff9 ("usb: chipidea: core: add sysfs group")
|
|
Oliver Neukum |
b7e0f0 |
cc: <stable@vger.kernel.org>
|
|
Oliver Neukum |
b7e0f0 |
Acked-by: Peter Chen <peter.chen@kernel.org>
|
|
Oliver Neukum |
b7e0f0 |
Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
|
|
Oliver Neukum |
b7e0f0 |
Link: https://lore.kernel.org/r/20230317061516.2451728-2-xu.yang_2@nxp.com
|
|
Oliver Neukum |
b7e0f0 |
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
Oliver Neukum |
b7e0f0 |
Signed-off-by: Oliver Neukum <oneukum@suse.com>
|
|
Oliver Neukum |
b7e0f0 |
---
|
|
Oliver Neukum |
b7e0f0 |
drivers/usb/chipidea/ci.h | 2 ++
|
|
Oliver Neukum |
b7e0f0 |
drivers/usb/chipidea/core.c | 4 ++++
|
|
Oliver Neukum |
b7e0f0 |
drivers/usb/chipidea/otg.c | 5 ++++-
|
|
Oliver Neukum |
b7e0f0 |
3 files changed, 10 insertions(+), 1 deletion(-)
|
|
Oliver Neukum |
b7e0f0 |
|
|
Oliver Neukum |
b7e0f0 |
--- a/drivers/usb/chipidea/ci.h
|
|
Oliver Neukum |
b7e0f0 |
+++ b/drivers/usb/chipidea/ci.h
|
|
Oliver Neukum |
b7e0f0 |
@@ -205,6 +205,7 @@ struct hw_bank {
|
|
Oliver Neukum |
b7e0f0 |
* @in_lpm: if the core in low power mode
|
|
Oliver Neukum |
b7e0f0 |
* @wakeup_int: if wakeup interrupt occur
|
|
Oliver Neukum |
b7e0f0 |
* @rev: The revision number for controller
|
|
Oliver Neukum |
b7e0f0 |
+ * @mutex: protect code from concorrent running when doing role switch
|
|
Oliver Neukum |
b7e0f0 |
*/
|
|
Oliver Neukum |
b7e0f0 |
struct ci_hdrc {
|
|
Oliver Neukum |
b7e0f0 |
struct device *dev;
|
|
Oliver Neukum |
b7e0f0 |
@@ -259,6 +260,7 @@ struct ci_hdrc {
|
|
Oliver Neukum |
b7e0f0 |
bool in_lpm;
|
|
Oliver Neukum |
b7e0f0 |
bool wakeup_int;
|
|
Oliver Neukum |
b7e0f0 |
enum ci_revision rev;
|
|
Oliver Neukum |
b7e0f0 |
+ struct mutex mutex;
|
|
Oliver Neukum |
b7e0f0 |
};
|
|
Oliver Neukum |
b7e0f0 |
|
|
Oliver Neukum |
b7e0f0 |
static inline struct ci_role_driver *ci_role(struct ci_hdrc *ci)
|
|
Oliver Neukum |
b7e0f0 |
--- a/drivers/usb/chipidea/core.c
|
|
Oliver Neukum |
b7e0f0 |
+++ b/drivers/usb/chipidea/core.c
|
|
Oliver Neukum |
b7e0f0 |
@@ -878,6 +878,8 @@ static ssize_t ci_role_store(struct devi
|
|
Oliver Neukum |
b7e0f0 |
if (role == CI_ROLE_END || role == ci->role)
|
|
Oliver Neukum |
b7e0f0 |
return -EINVAL;
|
|
Oliver Neukum |
b7e0f0 |
|
|
Oliver Neukum |
b7e0f0 |
+ mutex_lock(&ci->mutex);
|
|
Oliver Neukum |
b7e0f0 |
+
|
|
Oliver Neukum |
b7e0f0 |
pm_runtime_get_sync(dev);
|
|
Oliver Neukum |
b7e0f0 |
disable_irq(ci->irq);
|
|
Oliver Neukum |
b7e0f0 |
ci_role_stop(ci);
|
|
Oliver Neukum |
b7e0f0 |
@@ -886,6 +888,7 @@ static ssize_t ci_role_store(struct devi
|
|
Oliver Neukum |
b7e0f0 |
ci_handle_vbus_change(ci);
|
|
Oliver Neukum |
b7e0f0 |
enable_irq(ci->irq);
|
|
Oliver Neukum |
b7e0f0 |
pm_runtime_put_sync(dev);
|
|
Oliver Neukum |
b7e0f0 |
+ mutex_unlock(&ci->mutex);
|
|
Oliver Neukum |
b7e0f0 |
|
|
Oliver Neukum |
b7e0f0 |
return (ret == 0) ? n : ret;
|
|
Oliver Neukum |
b7e0f0 |
}
|
|
Oliver Neukum |
b7e0f0 |
@@ -924,6 +927,7 @@ static int ci_hdrc_probe(struct platform
|
|
Oliver Neukum |
b7e0f0 |
return -ENOMEM;
|
|
Oliver Neukum |
b7e0f0 |
|
|
Oliver Neukum |
b7e0f0 |
spin_lock_init(&ci->lock);
|
|
Oliver Neukum |
b7e0f0 |
+ mutex_init(&ci->mutex);
|
|
Oliver Neukum |
b7e0f0 |
ci->dev = dev;
|
|
Oliver Neukum |
b7e0f0 |
ci->platdata = dev_get_platdata(dev);
|
|
Oliver Neukum |
b7e0f0 |
ci->imx28_write_fix = !!(ci->platdata->flags &
|
|
Oliver Neukum |
b7e0f0 |
--- a/drivers/usb/chipidea/otg.c
|
|
Oliver Neukum |
b7e0f0 |
+++ b/drivers/usb/chipidea/otg.c
|
|
Oliver Neukum |
b7e0f0 |
@@ -167,8 +167,10 @@ static int hw_wait_vbus_lower_bsv(struct
|
|
Oliver Neukum |
b7e0f0 |
|
|
Oliver Neukum |
b7e0f0 |
static void ci_handle_id_switch(struct ci_hdrc *ci)
|
|
Oliver Neukum |
b7e0f0 |
{
|
|
Oliver Neukum |
b7e0f0 |
- enum ci_role role = ci_otg_role(ci);
|
|
Oliver Neukum |
b7e0f0 |
+ enum ci_role role;
|
|
Oliver Neukum |
b7e0f0 |
|
|
Oliver Neukum |
b7e0f0 |
+ mutex_lock(&ci->mutex);
|
|
Oliver Neukum |
b7e0f0 |
+ role = ci_otg_role(ci);
|
|
Oliver Neukum |
b7e0f0 |
if (role != ci->role) {
|
|
Oliver Neukum |
b7e0f0 |
dev_dbg(ci->dev, "switching from %s to %s\n",
|
|
Oliver Neukum |
b7e0f0 |
ci_role(ci)->name, ci->roles[role]->name);
|
|
Oliver Neukum |
b7e0f0 |
@@ -191,6 +193,7 @@ static void ci_handle_id_switch(struct c
|
|
Oliver Neukum |
b7e0f0 |
if (role == CI_ROLE_GADGET)
|
|
Oliver Neukum |
b7e0f0 |
ci_handle_vbus_change(ci);
|
|
Oliver Neukum |
b7e0f0 |
}
|
|
Oliver Neukum |
b7e0f0 |
+ mutex_unlock(&ci->mutex);
|
|
Oliver Neukum |
b7e0f0 |
}
|
|
Oliver Neukum |
b7e0f0 |
/**
|
|
Oliver Neukum |
b7e0f0 |
* ci_otg_work - perform otg (vbus/id) event handle
|