]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
6.1-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 22 Jul 2025 08:32:32 +0000 (10:32 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 22 Jul 2025 08:32:32 +0000 (10:32 +0200)
added patches:
usb-hub-don-t-try-to-recover-devices-lost-during-warm-reset.patch
usb-hub-fix-detection-of-high-tier-usb3-devices-behind-suspended-hubs.patch
usb-hub-fix-flushing-and-scheduling-of-delayed-work-that-tunes-runtime-pm.patch
usb-hub-fix-flushing-of-delayed-work-used-for-post-resume-purposes.patch
usb-musb-add-and-use-inline-functions-musb_-get-set-_state.patch
usb-musb-fix-gadget-state-on-disconnect.patch

queue-6.1/series
queue-6.1/usb-hub-don-t-try-to-recover-devices-lost-during-warm-reset.patch [new file with mode: 0644]
queue-6.1/usb-hub-fix-detection-of-high-tier-usb3-devices-behind-suspended-hubs.patch [new file with mode: 0644]
queue-6.1/usb-hub-fix-flushing-and-scheduling-of-delayed-work-that-tunes-runtime-pm.patch [new file with mode: 0644]
queue-6.1/usb-hub-fix-flushing-of-delayed-work-used-for-post-resume-purposes.patch [new file with mode: 0644]
queue-6.1/usb-musb-add-and-use-inline-functions-musb_-get-set-_state.patch [new file with mode: 0644]
queue-6.1/usb-musb-fix-gadget-state-on-disconnect.patch [new file with mode: 0644]

index 5b628d1b69af1529446f89a1b225556b3428c8a3..2fd21a263f39421ac4b7dc92294d515b9167c2c9 100644 (file)
@@ -66,3 +66,9 @@ revert-cgroup_freezer-cgroup_freezing-check-if-not-f.patch
 sched-change-nr_uninterruptible-type-to-unsigned-long.patch
 hid-mcp2221-set-driver-data-before-i2c-adapter-add.patch
 clone_private_mnt-make-sure-that-caller-has-cap_sys_admin-in-the-right-userns.patch
+usb-hub-fix-detection-of-high-tier-usb3-devices-behind-suspended-hubs.patch
+usb-hub-fix-flushing-and-scheduling-of-delayed-work-that-tunes-runtime-pm.patch
+usb-hub-fix-flushing-of-delayed-work-used-for-post-resume-purposes.patch
+usb-hub-don-t-try-to-recover-devices-lost-during-warm-reset.patch
+usb-musb-add-and-use-inline-functions-musb_-get-set-_state.patch
+usb-musb-fix-gadget-state-on-disconnect.patch
diff --git a/queue-6.1/usb-hub-don-t-try-to-recover-devices-lost-during-warm-reset.patch b/queue-6.1/usb-hub-don-t-try-to-recover-devices-lost-during-warm-reset.patch
new file mode 100644 (file)
index 0000000..e7d38dd
--- /dev/null
@@ -0,0 +1,89 @@
+From 2521106fc732b0b75fd3555c689b1ed1d29d273c Mon Sep 17 00:00:00 2001
+From: Mathias Nyman <mathias.nyman@linux.intel.com>
+Date: Mon, 23 Jun 2025 16:39:47 +0300
+Subject: usb: hub: Don't try to recover devices lost during warm reset.
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Mathias Nyman <mathias.nyman@linux.intel.com>
+
+commit 2521106fc732b0b75fd3555c689b1ed1d29d273c upstream.
+
+Hub driver warm-resets ports in SS.Inactive or Compliance mode to
+recover a possible connected device. The port reset code correctly
+detects if a connection is lost during reset, but hub driver
+port_event() fails to take this into account in some cases.
+port_event() ends up using stale values and assumes there is a
+connected device, and will try all means to recover it, including
+power-cycling the port.
+
+Details:
+This case was triggered when xHC host was suspended with DbC (Debug
+Capability) enabled and connected. DbC turns one xHC port into a simple
+usb debug device, allowing debugging a system with an A-to-A USB debug
+cable.
+
+xhci DbC code disables DbC when xHC is system suspended to D3, and
+enables it back during resume.
+We essentially end up with two hosts connected to each other during
+suspend, and, for a short while during resume, until DbC is enabled back.
+The suspended xHC host notices some activity on the roothub port, but
+can't train the link due to being suspended, so xHC hardware sets a CAS
+(Cold Attach Status) flag for this port to inform xhci host driver that
+the port needs to be warm reset once xHC resumes.
+
+CAS is xHCI specific, and not part of USB specification, so xhci driver
+tells usb core that the port has a connection and link is in compliance
+mode. Recovery from complinace mode is similar to CAS recovery.
+
+xhci CAS driver support that fakes a compliance mode connection was added
+in commit 8bea2bd37df0 ("usb: Add support for root hub port status CAS")
+
+Once xHCI resumes and DbC is enabled back, all activity on the xHC
+roothub host side port disappears. The hub driver will anyway think
+port has a connection and link is in compliance mode, and hub driver
+will try to recover it.
+
+The port power-cycle during recovery seems to cause issues to the active
+DbC connection.
+
+Fix this by clearing connect_change flag if hub_port_reset() returns
+-ENOTCONN, thus avoiding the whole unnecessary port recovery and
+initialization attempt.
+
+Cc: stable@vger.kernel.org
+Fixes: 8bea2bd37df0 ("usb: Add support for root hub port status CAS")
+Tested-by: Ćukasz Bartosik <ukaszb@chromium.org>
+Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
+Acked-by: Alan Stern <stern@rowland.harvard.edu>
+Link: https://lore.kernel.org/r/20250623133947.3144608-1-mathias.nyman@linux.intel.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/core/hub.c |    8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+--- a/drivers/usb/core/hub.c
++++ b/drivers/usb/core/hub.c
+@@ -5702,6 +5702,7 @@ static void port_event(struct usb_hub *h
+       struct usb_device *hdev = hub->hdev;
+       u16 portstatus, portchange;
+       int i = 0;
++      int err;
+       connect_change = test_bit(port1, hub->change_bits);
+       clear_bit(port1, hub->event_bits);
+@@ -5794,8 +5795,11 @@ static void port_event(struct usb_hub *h
+               } else if (!udev || !(portstatus & USB_PORT_STAT_CONNECTION)
+                               || udev->state == USB_STATE_NOTATTACHED) {
+                       dev_dbg(&port_dev->dev, "do warm reset, port only\n");
+-                      if (hub_port_reset(hub, port1, NULL,
+-                                      HUB_BH_RESET_TIME, true) < 0)
++                      err = hub_port_reset(hub, port1, NULL,
++                                           HUB_BH_RESET_TIME, true);
++                      if (!udev && err == -ENOTCONN)
++                              connect_change = 0;
++                      else if (err < 0)
+                               hub_port_disable(hub, port1, 1);
+               } else {
+                       dev_dbg(&port_dev->dev, "do warm reset, full device\n");
diff --git a/queue-6.1/usb-hub-fix-detection-of-high-tier-usb3-devices-behind-suspended-hubs.patch b/queue-6.1/usb-hub-fix-detection-of-high-tier-usb3-devices-behind-suspended-hubs.patch
new file mode 100644 (file)
index 0000000..821de33
--- /dev/null
@@ -0,0 +1,124 @@
+From 8f5b7e2bec1c36578fdaa74a6951833541103e27 Mon Sep 17 00:00:00 2001
+From: Mathias Nyman <mathias.nyman@linux.intel.com>
+Date: Wed, 11 Jun 2025 14:24:41 +0300
+Subject: usb: hub: fix detection of high tier USB3 devices behind suspended hubs
+
+From: Mathias Nyman <mathias.nyman@linux.intel.com>
+
+commit 8f5b7e2bec1c36578fdaa74a6951833541103e27 upstream.
+
+USB3 devices connected behind several external suspended hubs may not
+be detected when plugged in due to aggressive hub runtime pm suspend.
+
+The hub driver immediately runtime-suspends hubs if there are no
+active children or port activity.
+
+There is a delay between the wake signal causing hub resume, and driver
+visible port activity on the hub downstream facing ports.
+Most of the LFPS handshake, resume signaling and link training done
+on the downstream ports is not visible to the hub driver until completed,
+when device then will appear fully enabled and running on the port.
+
+This delay between wake signal and detectable port change is even more
+significant with chained suspended hubs where the wake signal will
+propagate upstream first. Suspended hubs will only start resuming
+downstream ports after upstream facing port resumes.
+
+The hub driver may resume a USB3 hub, read status of all ports, not
+yet see any activity, and runtime suspend back the hub before any
+port activity is visible.
+
+This exact case was seen when conncting USB3 devices to a suspended
+Thunderbolt dock.
+
+USB3 specification defines a 100ms tU3WakeupRetryDelay, indicating
+USB3 devices expect to be resumed within 100ms after signaling wake.
+if not then device will resend the wake signal.
+
+Give the USB3 hubs twice this time (200ms) to detect any port
+changes after resume, before allowing hub to runtime suspend again.
+
+Cc: stable <stable@kernel.org>
+Fixes: 2839f5bcfcfc ("USB: Turn on auto-suspend for USB 3.0 hubs.")
+Acked-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
+Link: https://lore.kernel.org/r/20250611112441.2267883-1-mathias.nyman@linux.intel.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/core/hub.c |   33 ++++++++++++++++++++++++++++++++-
+ 1 file changed, 32 insertions(+), 1 deletion(-)
+
+--- a/drivers/usb/core/hub.c
++++ b/drivers/usb/core/hub.c
+@@ -66,6 +66,12 @@
+  */
+ #define USB_SHORT_SET_ADDRESS_REQ_TIMEOUT     500  /* ms */
++/*
++ * Give SS hubs 200ms time after wake to train downstream links before
++ * assuming no port activity and allowing hub to runtime suspend back.
++ */
++#define USB_SS_PORT_U0_WAKE_TIME      200  /* ms */
++
+ /* Protect struct usb_device->state and ->children members
+  * Note: Both are also protected by ->dev.sem, except that ->state can
+  * change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */
+@@ -1038,11 +1044,12 @@ int usb_remove_device(struct usb_device
+ enum hub_activation_type {
+       HUB_INIT, HUB_INIT2, HUB_INIT3,         /* INITs must come first */
+-      HUB_POST_RESET, HUB_RESUME, HUB_RESET_RESUME,
++      HUB_POST_RESET, HUB_RESUME, HUB_RESET_RESUME, HUB_POST_RESUME,
+ };
+ static void hub_init_func2(struct work_struct *ws);
+ static void hub_init_func3(struct work_struct *ws);
++static void hub_post_resume(struct work_struct *ws);
+ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
+ {
+@@ -1065,6 +1072,13 @@ static void hub_activate(struct usb_hub
+                       goto init2;
+               goto init3;
+       }
++
++      if (type == HUB_POST_RESUME) {
++              usb_autopm_put_interface_async(to_usb_interface(hub->intfdev));
++              hub_put(hub);
++              return;
++      }
++
+       hub_get(hub);
+       /* The superspeed hub except for root hub has to use Hub Depth
+@@ -1313,6 +1327,16 @@ static void hub_activate(struct usb_hub
+               device_unlock(&hdev->dev);
+       }
++      if (type == HUB_RESUME && hub_is_superspeed(hub->hdev)) {
++              /* give usb3 downstream links training time after hub resume */
++              INIT_DELAYED_WORK(&hub->init_work, hub_post_resume);
++              queue_delayed_work(system_power_efficient_wq, &hub->init_work,
++                                 msecs_to_jiffies(USB_SS_PORT_U0_WAKE_TIME));
++              usb_autopm_get_interface_no_resume(
++                      to_usb_interface(hub->intfdev));
++              return;
++      }
++
+       hub_put(hub);
+ }
+@@ -1331,6 +1355,13 @@ static void hub_init_func3(struct work_s
+       hub_activate(hub, HUB_INIT3);
+ }
++static void hub_post_resume(struct work_struct *ws)
++{
++      struct usb_hub *hub = container_of(ws, struct usb_hub, init_work.work);
++
++      hub_activate(hub, HUB_POST_RESUME);
++}
++
+ enum hub_quiescing_type {
+       HUB_DISCONNECT, HUB_PRE_RESET, HUB_SUSPEND
+ };
diff --git a/queue-6.1/usb-hub-fix-flushing-and-scheduling-of-delayed-work-that-tunes-runtime-pm.patch b/queue-6.1/usb-hub-fix-flushing-and-scheduling-of-delayed-work-that-tunes-runtime-pm.patch
new file mode 100644 (file)
index 0000000..8b98c9d
--- /dev/null
@@ -0,0 +1,63 @@
+From a49e1e2e785fb3621f2d748581881b23a364998a Mon Sep 17 00:00:00 2001
+From: Mathias Nyman <mathias.nyman@linux.intel.com>
+Date: Thu, 26 Jun 2025 16:01:02 +0300
+Subject: usb: hub: Fix flushing and scheduling of delayed work that tunes runtime pm
+
+From: Mathias Nyman <mathias.nyman@linux.intel.com>
+
+commit a49e1e2e785fb3621f2d748581881b23a364998a upstream.
+
+Delayed work to prevent USB3 hubs from runtime-suspending immediately
+after resume was added in commit 8f5b7e2bec1c ("usb: hub: fix detection
+of high tier USB3 devices behind suspended hubs").
+
+This delayed work needs be flushed if system suspends, or hub needs to
+be quiesced for other reasons right after resume. Not flushing it
+triggered issues on QC SC8280XP CRD board during suspend/resume testing.
+
+Fix it by flushing the delayed resume work in hub_quiesce()
+
+The delayed work item that allow hub runtime suspend is also scheduled
+just before calling autopm get. Alan pointed out there is a small risk
+that work is run before autopm get, which would call autopm put before
+get, and mess up the runtime pm usage order.
+Swap the order of work sheduling and calling autopm get to solve this.
+
+Cc: stable <stable@kernel.org>
+Fixes: 8f5b7e2bec1c ("usb: hub: fix detection of high tier USB3 devices behind suspended hubs")
+Reported-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
+Closes: https://lore.kernel.org/linux-usb/acaaa928-832c-48ca-b0ea-d202d5cd3d6c@oss.qualcomm.com
+Reported-by: Alan Stern <stern@rowland.harvard.edu>
+Closes: https://lore.kernel.org/linux-usb/c73fbead-66d7-497a-8fa1-75ea4761090a@rowland.harvard.edu
+Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
+Link: https://lore.kernel.org/r/20250626130102.3639861-2-mathias.nyman@linux.intel.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/core/hub.c |    6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+--- a/drivers/usb/core/hub.c
++++ b/drivers/usb/core/hub.c
+@@ -1329,11 +1329,12 @@ static void hub_activate(struct usb_hub
+       if (type == HUB_RESUME && hub_is_superspeed(hub->hdev)) {
+               /* give usb3 downstream links training time after hub resume */
++              usb_autopm_get_interface_no_resume(
++                      to_usb_interface(hub->intfdev));
++
+               INIT_DELAYED_WORK(&hub->init_work, hub_post_resume);
+               queue_delayed_work(system_power_efficient_wq, &hub->init_work,
+                                  msecs_to_jiffies(USB_SS_PORT_U0_WAKE_TIME));
+-              usb_autopm_get_interface_no_resume(
+-                      to_usb_interface(hub->intfdev));
+               return;
+       }
+@@ -1387,6 +1388,7 @@ static void hub_quiesce(struct usb_hub *
+       /* Stop hub_wq and related activity */
+       del_timer_sync(&hub->irq_urb_retry);
++      flush_delayed_work(&hub->init_work);
+       usb_kill_urb(hub->urb);
+       if (hub->has_indicators)
+               cancel_delayed_work_sync(&hub->leds);
diff --git a/queue-6.1/usb-hub-fix-flushing-of-delayed-work-used-for-post-resume-purposes.patch b/queue-6.1/usb-hub-fix-flushing-of-delayed-work-used-for-post-resume-purposes.patch
new file mode 100644 (file)
index 0000000..74e9720
--- /dev/null
@@ -0,0 +1,117 @@
+From 9bd9c8026341f75f25c53104eb7e656e357ca1a2 Mon Sep 17 00:00:00 2001
+From: Mathias Nyman <mathias.nyman@linux.intel.com>
+Date: Fri, 27 Jun 2025 19:43:48 +0300
+Subject: usb: hub: Fix flushing of delayed work used for post resume purposes
+
+From: Mathias Nyman <mathias.nyman@linux.intel.com>
+
+commit 9bd9c8026341f75f25c53104eb7e656e357ca1a2 upstream.
+
+Delayed work that prevents USB3 hubs from runtime-suspending too early
+needed to be flushed in hub_quiesce() to resolve issues detected on
+QC SC8280XP CRD board during suspend resume testing.
+
+This flushing did however trigger new issues on Raspberry Pi 3B+, which
+doesn't have USB3 ports, and doesn't queue any post resume delayed work.
+
+The flushed 'hub->init_work' item is used for several purposes, and
+is originally initialized with a 'NULL' work function. The work function
+is also changed on the fly, which may contribute to the issue.
+
+Solve this by creating a dedicated delayed work item for post resume work,
+and flush that delayed work in hub_quiesce()
+
+Cc: stable <stable@kernel.org>
+Fixes: a49e1e2e785f ("usb: hub: Fix flushing and scheduling of delayed work that tunes runtime pm")
+Reported-by: Mark Brown <broonie@kernel.org>
+Closes: https://lore.kernel.org/linux-usb/aF5rNp1l0LWITnEB@finisterre.sirena.org.uk
+Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
+Tested-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com> # SC8280XP CRD
+Tested-by: Mark Brown <broonie@kernel.org>
+Link: https://lore.kernel.org/r/20250627164348.3982628-2-mathias.nyman@linux.intel.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/core/hub.c |   21 ++++++++-------------
+ drivers/usb/core/hub.h |    1 +
+ 2 files changed, 9 insertions(+), 13 deletions(-)
+
+--- a/drivers/usb/core/hub.c
++++ b/drivers/usb/core/hub.c
+@@ -1044,12 +1044,11 @@ int usb_remove_device(struct usb_device
+ enum hub_activation_type {
+       HUB_INIT, HUB_INIT2, HUB_INIT3,         /* INITs must come first */
+-      HUB_POST_RESET, HUB_RESUME, HUB_RESET_RESUME, HUB_POST_RESUME,
++      HUB_POST_RESET, HUB_RESUME, HUB_RESET_RESUME,
+ };
+ static void hub_init_func2(struct work_struct *ws);
+ static void hub_init_func3(struct work_struct *ws);
+-static void hub_post_resume(struct work_struct *ws);
+ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
+ {
+@@ -1073,12 +1072,6 @@ static void hub_activate(struct usb_hub
+               goto init3;
+       }
+-      if (type == HUB_POST_RESUME) {
+-              usb_autopm_put_interface_async(to_usb_interface(hub->intfdev));
+-              hub_put(hub);
+-              return;
+-      }
+-
+       hub_get(hub);
+       /* The superspeed hub except for root hub has to use Hub Depth
+@@ -1332,8 +1325,8 @@ static void hub_activate(struct usb_hub
+               usb_autopm_get_interface_no_resume(
+                       to_usb_interface(hub->intfdev));
+-              INIT_DELAYED_WORK(&hub->init_work, hub_post_resume);
+-              queue_delayed_work(system_power_efficient_wq, &hub->init_work,
++              queue_delayed_work(system_power_efficient_wq,
++                                 &hub->post_resume_work,
+                                  msecs_to_jiffies(USB_SS_PORT_U0_WAKE_TIME));
+               return;
+       }
+@@ -1358,9 +1351,10 @@ static void hub_init_func3(struct work_s
+ static void hub_post_resume(struct work_struct *ws)
+ {
+-      struct usb_hub *hub = container_of(ws, struct usb_hub, init_work.work);
++      struct usb_hub *hub = container_of(ws, struct usb_hub, post_resume_work.work);
+-      hub_activate(hub, HUB_POST_RESUME);
++      usb_autopm_put_interface_async(to_usb_interface(hub->intfdev));
++      hub_put(hub);
+ }
+ enum hub_quiescing_type {
+@@ -1388,7 +1382,7 @@ static void hub_quiesce(struct usb_hub *
+       /* Stop hub_wq and related activity */
+       del_timer_sync(&hub->irq_urb_retry);
+-      flush_delayed_work(&hub->init_work);
++      flush_delayed_work(&hub->post_resume_work);
+       usb_kill_urb(hub->urb);
+       if (hub->has_indicators)
+               cancel_delayed_work_sync(&hub->leds);
+@@ -1947,6 +1941,7 @@ static int hub_probe(struct usb_interfac
+       hub->hdev = hdev;
+       INIT_DELAYED_WORK(&hub->leds, led_work);
+       INIT_DELAYED_WORK(&hub->init_work, NULL);
++      INIT_DELAYED_WORK(&hub->post_resume_work, hub_post_resume);
+       INIT_WORK(&hub->events, hub_event);
+       INIT_LIST_HEAD(&hub->onboard_hub_devs);
+       spin_lock_init(&hub->irq_urb_lock);
+--- a/drivers/usb/core/hub.h
++++ b/drivers/usb/core/hub.h
+@@ -69,6 +69,7 @@ struct usb_hub {
+       u8                      indicator[USB_MAXCHILDREN];
+       struct delayed_work     leds;
+       struct delayed_work     init_work;
++      struct delayed_work     post_resume_work;
+       struct work_struct      events;
+       spinlock_t              irq_urb_lock;
+       struct timer_list       irq_urb_retry;
diff --git a/queue-6.1/usb-musb-add-and-use-inline-functions-musb_-get-set-_state.patch b/queue-6.1/usb-musb-add-and-use-inline-functions-musb_-get-set-_state.patch
new file mode 100644 (file)
index 0000000..6b01a54
--- /dev/null
@@ -0,0 +1,532 @@
+From 21acc656a06e912341d9db66c67b58cc7ed071e7 Mon Sep 17 00:00:00 2001
+From: Paul Cercueil <paul@crapouillou.net>
+Date: Wed, 26 Oct 2022 19:26:51 +0100
+Subject: usb: musb: Add and use inline functions musb_{get,set}_state
+
+From: Paul Cercueil <paul@crapouillou.net>
+
+commit 21acc656a06e912341d9db66c67b58cc7ed071e7 upstream.
+
+Instead of manipulating musb->xceiv->otg->state directly, use the newly
+introduced musb_get_state() and musb_set_state() inline functions.
+
+Later, these inline functions will be modified to get rid of the
+musb->xceiv dependency, which prevents the musb code from using the
+generic PHY subsystem.
+
+Signed-off-by: Paul Cercueil <paul@crapouillou.net>
+Link: https://lore.kernel.org/r/20221026182657.146630-2-paul@crapouillou.net
+Stable-dep-of: 67a59f82196c ("usb: musb: fix gadget state on disconnect")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/musb/musb_core.c    |   62 ++++++++++++++++++++--------------------
+ drivers/usb/musb/musb_core.h    |   11 +++++++
+ drivers/usb/musb/musb_debugfs.c |    6 +--
+ drivers/usb/musb/musb_gadget.c  |   28 +++++++++---------
+ drivers/usb/musb/musb_host.c    |    6 +--
+ drivers/usb/musb/musb_virthub.c |   18 +++++------
+ 6 files changed, 71 insertions(+), 60 deletions(-)
+
+--- a/drivers/usb/musb/musb_core.c
++++ b/drivers/usb/musb/musb_core.c
+@@ -502,7 +502,7 @@ int musb_set_host(struct musb *musb)
+ init_data:
+       musb->is_active = 1;
+-      musb->xceiv->otg->state = OTG_STATE_A_IDLE;
++      musb_set_state(musb, OTG_STATE_A_IDLE);
+       MUSB_HST_MODE(musb);
+       return error;
+@@ -549,7 +549,7 @@ int musb_set_peripheral(struct musb *mus
+ init_data:
+       musb->is_active = 0;
+-      musb->xceiv->otg->state = OTG_STATE_B_IDLE;
++      musb_set_state(musb, OTG_STATE_B_IDLE);
+       MUSB_DEV_MODE(musb);
+       return error;
+@@ -599,12 +599,12 @@ static void musb_otg_timer_func(struct t
+       unsigned long   flags;
+       spin_lock_irqsave(&musb->lock, flags);
+-      switch (musb->xceiv->otg->state) {
++      switch (musb_get_state(musb)) {
+       case OTG_STATE_B_WAIT_ACON:
+               musb_dbg(musb,
+                       "HNP: b_wait_acon timeout; back to b_peripheral");
+               musb_g_disconnect(musb);
+-              musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
++              musb_set_state(musb, OTG_STATE_B_PERIPHERAL);
+               musb->is_active = 0;
+               break;
+       case OTG_STATE_A_SUSPEND:
+@@ -612,7 +612,7 @@ static void musb_otg_timer_func(struct t
+               musb_dbg(musb, "HNP: %s timeout",
+                       usb_otg_state_string(musb->xceiv->otg->state));
+               musb_platform_set_vbus(musb, 0);
+-              musb->xceiv->otg->state = OTG_STATE_A_WAIT_VFALL;
++              musb_set_state(musb, OTG_STATE_A_WAIT_VFALL);
+               break;
+       default:
+               musb_dbg(musb, "HNP: Unhandled mode %s",
+@@ -633,7 +633,7 @@ void musb_hnp_stop(struct musb *musb)
+       musb_dbg(musb, "HNP: stop from %s",
+                       usb_otg_state_string(musb->xceiv->otg->state));
+-      switch (musb->xceiv->otg->state) {
++      switch (musb_get_state(musb)) {
+       case OTG_STATE_A_PERIPHERAL:
+               musb_g_disconnect(musb);
+               musb_dbg(musb, "HNP: back to %s",
+@@ -643,7 +643,7 @@ void musb_hnp_stop(struct musb *musb)
+               musb_dbg(musb, "HNP: Disabling HR");
+               if (hcd)
+                       hcd->self.is_b_host = 0;
+-              musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
++              musb_set_state(musb, OTG_STATE_B_PERIPHERAL);
+               MUSB_DEV_MODE(musb);
+               reg = musb_readb(mbase, MUSB_POWER);
+               reg |= MUSB_POWER_SUSPENDM;
+@@ -671,7 +671,7 @@ static void musb_handle_intr_resume(stru
+                       usb_otg_state_string(musb->xceiv->otg->state));
+       if (devctl & MUSB_DEVCTL_HM) {
+-              switch (musb->xceiv->otg->state) {
++              switch (musb_get_state(musb)) {
+               case OTG_STATE_A_SUSPEND:
+                       /* remote wakeup? */
+                       musb->port1_status |=
+@@ -679,14 +679,14 @@ static void musb_handle_intr_resume(stru
+                                       | MUSB_PORT_STAT_RESUME;
+                       musb->rh_timer = jiffies
+                               + msecs_to_jiffies(USB_RESUME_TIMEOUT);
+-                      musb->xceiv->otg->state = OTG_STATE_A_HOST;
++                      musb_set_state(musb, OTG_STATE_A_HOST);
+                       musb->is_active = 1;
+                       musb_host_resume_root_hub(musb);
+                       schedule_delayed_work(&musb->finish_resume_work,
+                               msecs_to_jiffies(USB_RESUME_TIMEOUT));
+                       break;
+               case OTG_STATE_B_WAIT_ACON:
+-                      musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
++                      musb_set_state(musb, OTG_STATE_B_PERIPHERAL);
+                       musb->is_active = 1;
+                       MUSB_DEV_MODE(musb);
+                       break;
+@@ -696,10 +696,10 @@ static void musb_handle_intr_resume(stru
+                               usb_otg_state_string(musb->xceiv->otg->state));
+               }
+       } else {
+-              switch (musb->xceiv->otg->state) {
++              switch (musb_get_state(musb)) {
+               case OTG_STATE_A_SUSPEND:
+                       /* possibly DISCONNECT is upcoming */
+-                      musb->xceiv->otg->state = OTG_STATE_A_HOST;
++                      musb_set_state(musb, OTG_STATE_A_HOST);
+                       musb_host_resume_root_hub(musb);
+                       break;
+               case OTG_STATE_B_WAIT_ACON:
+@@ -750,7 +750,7 @@ static irqreturn_t musb_handle_intr_sess
+        */
+       musb_writeb(mbase, MUSB_DEVCTL, MUSB_DEVCTL_SESSION);
+       musb->ep0_stage = MUSB_EP0_START;
+-      musb->xceiv->otg->state = OTG_STATE_A_IDLE;
++      musb_set_state(musb, OTG_STATE_A_IDLE);
+       MUSB_HST_MODE(musb);
+       musb_platform_set_vbus(musb, 1);
+@@ -777,7 +777,7 @@ static void musb_handle_intr_vbuserr(str
+        * REVISIT:  do delays from lots of DEBUG_KERNEL checks
+        * make trouble here, keeping VBUS < 4.4V ?
+        */
+-      switch (musb->xceiv->otg->state) {
++      switch (musb_get_state(musb)) {
+       case OTG_STATE_A_HOST:
+               /* recovery is dicey once we've gotten past the
+                * initial stages of enumeration, but if VBUS
+@@ -833,7 +833,7 @@ static void musb_handle_intr_suspend(str
+       musb_dbg(musb, "SUSPEND (%s) devctl %02x",
+               usb_otg_state_string(musb->xceiv->otg->state), devctl);
+-      switch (musb->xceiv->otg->state) {
++      switch (musb_get_state(musb)) {
+       case OTG_STATE_A_PERIPHERAL:
+               /* We also come here if the cable is removed, since
+                * this silicon doesn't report ID-no-longer-grounded.
+@@ -858,7 +858,7 @@ static void musb_handle_intr_suspend(str
+               musb_g_suspend(musb);
+               musb->is_active = musb->g.b_hnp_enable;
+               if (musb->is_active) {
+-                      musb->xceiv->otg->state = OTG_STATE_B_WAIT_ACON;
++                      musb_set_state(musb, OTG_STATE_B_WAIT_ACON);
+                       musb_dbg(musb, "HNP: Setting timer for b_ase0_brst");
+                       mod_timer(&musb->otg_timer, jiffies
+                               + msecs_to_jiffies(
+@@ -871,7 +871,7 @@ static void musb_handle_intr_suspend(str
+                               + msecs_to_jiffies(musb->a_wait_bcon));
+               break;
+       case OTG_STATE_A_HOST:
+-              musb->xceiv->otg->state = OTG_STATE_A_SUSPEND;
++              musb_set_state(musb, OTG_STATE_A_SUSPEND);
+               musb->is_active = musb->hcd->self.b_hnp_enable;
+               break;
+       case OTG_STATE_B_HOST:
+@@ -909,7 +909,7 @@ static void musb_handle_intr_connect(str
+               musb->port1_status |= USB_PORT_STAT_LOW_SPEED;
+       /* indicate new connection to OTG machine */
+-      switch (musb->xceiv->otg->state) {
++      switch (musb_get_state(musb)) {
+       case OTG_STATE_B_PERIPHERAL:
+               if (int_usb & MUSB_INTR_SUSPEND) {
+                       musb_dbg(musb, "HNP: SUSPEND+CONNECT, now b_host");
+@@ -921,7 +921,7 @@ static void musb_handle_intr_connect(str
+       case OTG_STATE_B_WAIT_ACON:
+               musb_dbg(musb, "HNP: CONNECT, now b_host");
+ b_host:
+-              musb->xceiv->otg->state = OTG_STATE_B_HOST;
++              musb_set_state(musb, OTG_STATE_B_HOST);
+               if (musb->hcd)
+                       musb->hcd->self.is_b_host = 1;
+               del_timer(&musb->otg_timer);
+@@ -929,7 +929,7 @@ b_host:
+       default:
+               if ((devctl & MUSB_DEVCTL_VBUS)
+                               == (3 << MUSB_DEVCTL_VBUS_SHIFT)) {
+-                      musb->xceiv->otg->state = OTG_STATE_A_HOST;
++                      musb_set_state(musb, OTG_STATE_A_HOST);
+                       if (hcd)
+                               hcd->self.is_b_host = 0;
+               }
+@@ -948,7 +948,7 @@ static void musb_handle_intr_disconnect(
+                       usb_otg_state_string(musb->xceiv->otg->state),
+                       MUSB_MODE(musb), devctl);
+-      switch (musb->xceiv->otg->state) {
++      switch (musb_get_state(musb)) {
+       case OTG_STATE_A_HOST:
+       case OTG_STATE_A_SUSPEND:
+               musb_host_resume_root_hub(musb);
+@@ -966,7 +966,7 @@ static void musb_handle_intr_disconnect(
+               musb_root_disconnect(musb);
+               if (musb->hcd)
+                       musb->hcd->self.is_b_host = 0;
+-              musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
++              musb_set_state(musb, OTG_STATE_B_PERIPHERAL);
+               MUSB_DEV_MODE(musb);
+               musb_g_disconnect(musb);
+               break;
+@@ -1006,7 +1006,7 @@ static void musb_handle_intr_reset(struc
+       } else {
+               musb_dbg(musb, "BUS RESET as %s",
+                       usb_otg_state_string(musb->xceiv->otg->state));
+-              switch (musb->xceiv->otg->state) {
++              switch (musb_get_state(musb)) {
+               case OTG_STATE_A_SUSPEND:
+                       musb_g_reset(musb);
+                       fallthrough;
+@@ -1025,11 +1025,11 @@ static void musb_handle_intr_reset(struc
+               case OTG_STATE_B_WAIT_ACON:
+                       musb_dbg(musb, "HNP: RESET (%s), to b_peripheral",
+                               usb_otg_state_string(musb->xceiv->otg->state));
+-                      musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
++                      musb_set_state(musb, OTG_STATE_B_PERIPHERAL);
+                       musb_g_reset(musb);
+                       break;
+               case OTG_STATE_B_IDLE:
+-                      musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
++                      musb_set_state(musb, OTG_STATE_B_PERIPHERAL);
+                       fallthrough;
+               case OTG_STATE_B_PERIPHERAL:
+                       musb_g_reset(musb);
+@@ -1216,8 +1216,8 @@ void musb_start(struct musb *musb)
+        * (c) peripheral initiates, using SRP
+        */
+       if (musb->port_mode != MUSB_HOST &&
+-                      musb->xceiv->otg->state != OTG_STATE_A_WAIT_BCON &&
+-                      (devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS) {
++          musb_get_state(musb) != OTG_STATE_A_WAIT_BCON &&
++          (devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS) {
+               musb->is_active = 1;
+       } else {
+               devctl |= MUSB_DEVCTL_SESSION;
+@@ -1908,7 +1908,7 @@ vbus_store(struct device *dev, struct de
+       spin_lock_irqsave(&musb->lock, flags);
+       /* force T(a_wait_bcon) to be zero/unlimited *OR* valid */
+       musb->a_wait_bcon = val ? max_t(int, val, OTG_TIME_A_WAIT_BCON) : 0 ;
+-      if (musb->xceiv->otg->state == OTG_STATE_A_WAIT_BCON)
++      if (musb_get_state(musb) == OTG_STATE_A_WAIT_BCON)
+               musb->is_active = 0;
+       musb_platform_try_idle(musb, jiffies + msecs_to_jiffies(val));
+       spin_unlock_irqrestore(&musb->lock, flags);
+@@ -2089,8 +2089,8 @@ static void musb_irq_work(struct work_st
+       musb_pm_runtime_check_session(musb);
+-      if (musb->xceiv->otg->state != musb->xceiv_old_state) {
+-              musb->xceiv_old_state = musb->xceiv->otg->state;
++      if (musb_get_state(musb) != musb->xceiv_old_state) {
++              musb->xceiv_old_state = musb_get_state(musb);
+               sysfs_notify(&musb->controller->kobj, NULL, "mode");
+       }
+@@ -2532,7 +2532,7 @@ musb_init_controller(struct device *dev,
+       }
+       MUSB_DEV_MODE(musb);
+-      musb->xceiv->otg->state = OTG_STATE_B_IDLE;
++      musb_set_state(musb, OTG_STATE_B_IDLE);
+       switch (musb->port_mode) {
+       case MUSB_HOST:
+--- a/drivers/usb/musb/musb_core.h
++++ b/drivers/usb/musb/musb_core.h
+@@ -592,6 +592,17 @@ static inline void musb_platform_clear_e
+               musb->ops->clear_ep_rxintr(musb, epnum);
+ }
++static inline void musb_set_state(struct musb *musb,
++                                enum usb_otg_state otg_state)
++{
++      musb->xceiv->otg->state = otg_state;
++}
++
++static inline enum usb_otg_state musb_get_state(struct musb *musb)
++{
++      return musb->xceiv->otg->state;
++}
++
+ /*
+  * gets the "dr_mode" property from DT and converts it into musb_mode
+  * if the property is not found or not recognized returns MUSB_OTG
+--- a/drivers/usb/musb/musb_debugfs.c
++++ b/drivers/usb/musb/musb_debugfs.c
+@@ -235,7 +235,7 @@ static int musb_softconnect_show(struct
+       u8              reg;
+       int             connect;
+-      switch (musb->xceiv->otg->state) {
++      switch (musb_get_state(musb)) {
+       case OTG_STATE_A_HOST:
+       case OTG_STATE_A_WAIT_BCON:
+               pm_runtime_get_sync(musb->controller);
+@@ -275,7 +275,7 @@ static ssize_t musb_softconnect_write(st
+       pm_runtime_get_sync(musb->controller);
+       if (!strncmp(buf, "0", 1)) {
+-              switch (musb->xceiv->otg->state) {
++              switch (musb_get_state(musb)) {
+               case OTG_STATE_A_HOST:
+                       musb_root_disconnect(musb);
+                       reg = musb_readb(musb->mregs, MUSB_DEVCTL);
+@@ -286,7 +286,7 @@ static ssize_t musb_softconnect_write(st
+                       break;
+               }
+       } else if (!strncmp(buf, "1", 1)) {
+-              switch (musb->xceiv->otg->state) {
++              switch (musb_get_state(musb)) {
+               case OTG_STATE_A_WAIT_BCON:
+                       /*
+                        * musb_save_context() called in musb_runtime_suspend()
+--- a/drivers/usb/musb/musb_gadget.c
++++ b/drivers/usb/musb/musb_gadget.c
+@@ -1530,7 +1530,7 @@ static int musb_gadget_wakeup(struct usb
+       spin_lock_irqsave(&musb->lock, flags);
+-      switch (musb->xceiv->otg->state) {
++      switch (musb_get_state(musb)) {
+       case OTG_STATE_B_PERIPHERAL:
+               /* NOTE:  OTG state machine doesn't include B_SUSPENDED;
+                * that's part of the standard usb 1.1 state machine, and
+@@ -1792,7 +1792,7 @@ int musb_gadget_setup(struct musb *musb)
+       musb->g.speed = USB_SPEED_UNKNOWN;
+       MUSB_DEV_MODE(musb);
+-      musb->xceiv->otg->state = OTG_STATE_B_IDLE;
++      musb_set_state(musb, OTG_STATE_B_IDLE);
+       /* this "gadget" abstracts/virtualizes the controller */
+       musb->g.name = musb_driver_name;
+@@ -1857,7 +1857,7 @@ static int musb_gadget_start(struct usb_
+       musb->is_active = 1;
+       otg_set_peripheral(otg, &musb->g);
+-      musb->xceiv->otg->state = OTG_STATE_B_IDLE;
++      musb_set_state(musb, OTG_STATE_B_IDLE);
+       spin_unlock_irqrestore(&musb->lock, flags);
+       musb_start(musb);
+@@ -1902,7 +1902,7 @@ static int musb_gadget_stop(struct usb_g
+       (void) musb_gadget_vbus_draw(&musb->g, 0);
+-      musb->xceiv->otg->state = OTG_STATE_UNDEFINED;
++      musb_set_state(musb, OTG_STATE_UNDEFINED);
+       musb_stop(musb);
+       otg_set_peripheral(musb->xceiv->otg, NULL);
+@@ -1931,7 +1931,7 @@ static int musb_gadget_stop(struct usb_g
+ void musb_g_resume(struct musb *musb)
+ {
+       musb->is_suspended = 0;
+-      switch (musb->xceiv->otg->state) {
++      switch (musb_get_state(musb)) {
+       case OTG_STATE_B_IDLE:
+               break;
+       case OTG_STATE_B_WAIT_ACON:
+@@ -1957,10 +1957,10 @@ void musb_g_suspend(struct musb *musb)
+       devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+       musb_dbg(musb, "musb_g_suspend: devctl %02x", devctl);
+-      switch (musb->xceiv->otg->state) {
++      switch (musb_get_state(musb)) {
+       case OTG_STATE_B_IDLE:
+               if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS)
+-                      musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
++                      musb_set_state(musb, OTG_STATE_B_PERIPHERAL);
+               break;
+       case OTG_STATE_B_PERIPHERAL:
+               musb->is_suspended = 1;
+@@ -2006,22 +2006,22 @@ void musb_g_disconnect(struct musb *musb
+               spin_lock(&musb->lock);
+       }
+-      switch (musb->xceiv->otg->state) {
++      switch (musb_get_state(musb)) {
+       default:
+               musb_dbg(musb, "Unhandled disconnect %s, setting a_idle",
+                       usb_otg_state_string(musb->xceiv->otg->state));
+-              musb->xceiv->otg->state = OTG_STATE_A_IDLE;
++              musb_set_state(musb, OTG_STATE_A_IDLE);
+               MUSB_HST_MODE(musb);
+               break;
+       case OTG_STATE_A_PERIPHERAL:
+-              musb->xceiv->otg->state = OTG_STATE_A_WAIT_BCON;
++              musb_set_state(musb, OTG_STATE_A_WAIT_BCON);
+               MUSB_HST_MODE(musb);
+               break;
+       case OTG_STATE_B_WAIT_ACON:
+       case OTG_STATE_B_HOST:
+       case OTG_STATE_B_PERIPHERAL:
+       case OTG_STATE_B_IDLE:
+-              musb->xceiv->otg->state = OTG_STATE_B_IDLE;
++              musb_set_state(musb, OTG_STATE_B_IDLE);
+               break;
+       case OTG_STATE_B_SRP_INIT:
+               break;
+@@ -2085,13 +2085,13 @@ __acquires(musb->lock)
+                * In that case, do not rely on devctl for setting
+                * peripheral mode.
+                */
+-              musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
++              musb_set_state(musb, OTG_STATE_B_PERIPHERAL);
+               musb->g.is_a_peripheral = 0;
+       } else if (devctl & MUSB_DEVCTL_BDEVICE) {
+-              musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
++              musb_set_state(musb, OTG_STATE_B_PERIPHERAL);
+               musb->g.is_a_peripheral = 0;
+       } else {
+-              musb->xceiv->otg->state = OTG_STATE_A_PERIPHERAL;
++              musb_set_state(musb, OTG_STATE_A_PERIPHERAL);
+               musb->g.is_a_peripheral = 1;
+       }
+--- a/drivers/usb/musb/musb_host.c
++++ b/drivers/usb/musb/musb_host.c
+@@ -2508,7 +2508,7 @@ static int musb_bus_suspend(struct usb_h
+       if (!is_host_active(musb))
+               return 0;
+-      switch (musb->xceiv->otg->state) {
++      switch (musb_get_state(musb)) {
+       case OTG_STATE_A_SUSPEND:
+               return 0;
+       case OTG_STATE_A_WAIT_VRISE:
+@@ -2518,7 +2518,7 @@ static int musb_bus_suspend(struct usb_h
+                */
+               devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+               if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS)
+-                      musb->xceiv->otg->state = OTG_STATE_A_WAIT_BCON;
++                      musb_set_state(musb, OTG_STATE_A_WAIT_BCON);
+               break;
+       default:
+               break;
+@@ -2727,7 +2727,7 @@ int musb_host_setup(struct musb *musb, i
+       if (musb->port_mode == MUSB_HOST) {
+               MUSB_HST_MODE(musb);
+-              musb->xceiv->otg->state = OTG_STATE_A_IDLE;
++              musb_set_state(musb, OTG_STATE_A_IDLE);
+       }
+       otg_set_host(musb->xceiv->otg, &hcd->self);
+       /* don't support otg protocols */
+--- a/drivers/usb/musb/musb_virthub.c
++++ b/drivers/usb/musb/musb_virthub.c
+@@ -43,7 +43,7 @@ void musb_host_finish_resume(struct work
+       musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16;
+       usb_hcd_poll_rh_status(musb->hcd);
+       /* NOTE: it might really be A_WAIT_BCON ... */
+-      musb->xceiv->otg->state = OTG_STATE_A_HOST;
++      musb_set_state(musb, OTG_STATE_A_HOST);
+       spin_unlock_irqrestore(&musb->lock, flags);
+ }
+@@ -85,9 +85,9 @@ int musb_port_suspend(struct musb *musb,
+               musb_dbg(musb, "Root port suspended, power %02x", power);
+               musb->port1_status |= USB_PORT_STAT_SUSPEND;
+-              switch (musb->xceiv->otg->state) {
++              switch (musb_get_state(musb)) {
+               case OTG_STATE_A_HOST:
+-                      musb->xceiv->otg->state = OTG_STATE_A_SUSPEND;
++                      musb_set_state(musb, OTG_STATE_A_SUSPEND);
+                       musb->is_active = otg->host->b_hnp_enable;
+                       if (musb->is_active)
+                               mod_timer(&musb->otg_timer, jiffies
+@@ -96,7 +96,7 @@ int musb_port_suspend(struct musb *musb,
+                       musb_platform_try_idle(musb, 0);
+                       break;
+               case OTG_STATE_B_HOST:
+-                      musb->xceiv->otg->state = OTG_STATE_B_WAIT_ACON;
++                      musb_set_state(musb, OTG_STATE_B_WAIT_ACON);
+                       musb->is_active = otg->host->b_hnp_enable;
+                       musb_platform_try_idle(musb, 0);
+                       break;
+@@ -123,7 +123,7 @@ void musb_port_reset(struct musb *musb,
+       u8              power;
+       void __iomem    *mbase = musb->mregs;
+-      if (musb->xceiv->otg->state == OTG_STATE_B_IDLE) {
++      if (musb_get_state(musb) == OTG_STATE_B_IDLE) {
+               musb_dbg(musb, "HNP: Returning from HNP; no hub reset from b_idle");
+               musb->port1_status &= ~USB_PORT_STAT_RESET;
+               return;
+@@ -204,20 +204,20 @@ void musb_root_disconnect(struct musb *m
+       usb_hcd_poll_rh_status(musb->hcd);
+       musb->is_active = 0;
+-      switch (musb->xceiv->otg->state) {
++      switch (musb_get_state(musb)) {
+       case OTG_STATE_A_SUSPEND:
+               if (otg->host->b_hnp_enable) {
+-                      musb->xceiv->otg->state = OTG_STATE_A_PERIPHERAL;
++                      musb_set_state(musb, OTG_STATE_A_PERIPHERAL);
+                       musb->g.is_a_peripheral = 1;
+                       break;
+               }
+               fallthrough;
+       case OTG_STATE_A_HOST:
+-              musb->xceiv->otg->state = OTG_STATE_A_WAIT_BCON;
++              musb_set_state(musb, OTG_STATE_A_WAIT_BCON);
+               musb->is_active = 0;
+               break;
+       case OTG_STATE_A_WAIT_VFALL:
+-              musb->xceiv->otg->state = OTG_STATE_B_IDLE;
++              musb_set_state(musb, OTG_STATE_B_IDLE);
+               break;
+       default:
+               musb_dbg(musb, "host disconnect (%s)",
diff --git a/queue-6.1/usb-musb-fix-gadget-state-on-disconnect.patch b/queue-6.1/usb-musb-fix-gadget-state-on-disconnect.patch
new file mode 100644 (file)
index 0000000..7ed1961
--- /dev/null
@@ -0,0 +1,48 @@
+From 67a59f82196c8c4f50c83329f0577acfb1349b50 Mon Sep 17 00:00:00 2001
+From: Drew Hamilton <drew.hamilton@zetier.com>
+Date: Tue, 1 Jul 2025 11:41:26 -0400
+Subject: usb: musb: fix gadget state on disconnect
+
+From: Drew Hamilton <drew.hamilton@zetier.com>
+
+commit 67a59f82196c8c4f50c83329f0577acfb1349b50 upstream.
+
+When unplugging the USB cable or disconnecting a gadget in usb peripheral mode with
+echo "" > /sys/kernel/config/usb_gadget/<your_gadget>/UDC,
+/sys/class/udc/musb-hdrc.0/state does not change from USB_STATE_CONFIGURED.
+
+Testing on dwc2/3 shows they both update the state to USB_STATE_NOTATTACHED.
+
+Add calls to usb_gadget_set_state in musb_g_disconnect and musb_gadget_stop
+to fix both cases.
+
+Fixes: 49401f4169c0 ("usb: gadget: introduce gadget state tracking")
+Cc: stable@vger.kernel.org
+Co-authored-by: Yehowshua Immanuel <yehowshua.immanuel@twosixtech.com>
+Signed-off-by: Yehowshua Immanuel <yehowshua.immanuel@twosixtech.com>
+Signed-off-by: Drew Hamilton <drew.hamilton@zetier.com>
+Link: https://lore.kernel.org/r/20250701154126.8543-1-drew.hamilton@zetier.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/musb/musb_gadget.c |    2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/usb/musb/musb_gadget.c
++++ b/drivers/usb/musb/musb_gadget.c
+@@ -1916,6 +1916,7 @@ static int musb_gadget_stop(struct usb_g
+        * gadget driver here and have everything work;
+        * that currently misbehaves.
+        */
++      usb_gadget_set_state(g, USB_STATE_NOTATTACHED);
+       /* Force check of devctl register for PM runtime */
+       pm_runtime_mark_last_busy(musb->controller);
+@@ -2022,6 +2023,7 @@ void musb_g_disconnect(struct musb *musb
+       case OTG_STATE_B_PERIPHERAL:
+       case OTG_STATE_B_IDLE:
+               musb_set_state(musb, OTG_STATE_B_IDLE);
++              usb_gadget_set_state(&musb->g, USB_STATE_NOTATTACHED);
+               break;
+       case OTG_STATE_B_SRP_INIT:
+               break;