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