[PATCH v3 08/13] usb: hub: Power on connected M.2 E-key connectors with power sequencing API
Chen-Yu Tsai
wenst at chromium.org
Fri Jul 3 05:58:56 PDT 2026
On Fri, Jul 3, 2026 at 8:41 PM Bartosz Golaszewski <brgl at kernel.org> wrote:
>
> On Fri, 3 Jul 2026 13:03:09 +0200, Chen-Yu Tsai <wenst at chromium.org> said:
> > The new M.2 E-key connector can have a USB connection. For the USB device
> > on this connector to work, its power must be enabled and the W_DISABLE2#
> > signal deasserted. The connector driver handles this and provides a
> > toggle over the power sequencing API.
> >
> > This feature currently only supports a directly connected (no mux in
> > between) M.2 E-key connector. Existing USB connector types are not
> > covered. The USB A connector was recently added to the onboard devices
> > driver. USB B connectors have historically been managed by the USB
> > gadget or dual-role device controller drivers. USB C connectors are
> > handled by TCPM drivers.
> >
> > The power sequencing API does not know whether a power sequence provider
> > is not needed or not available yet, so we only request it for connectors
> > that we know need it, which at this time is just the E-key connector.
> >
> > On the USB side, the port firmware node (if present) is tied to the
> > usb_port device. This device is used to acquire the power sequencing
> > descriptor. This allows the provider to tell the different ports on one
> > hub apart.
> >
> > This feature is not implemented in the onboard USB devices driver. The
> > power sequencing API expects the consumer device to make the request,
> > but there is no device node to instantiate a platform device to tie
> > the driver to. The connector is not a child node of the USB host or
> > hub, and the graph connection is from a USB port to the connector.
> > And the connector itself already has a driver.
> >
> > Power sequencing is not directly enabled in the connector driver as
> > that would completely decouple the timing of it from the USB subsystem.
> > It would not be possible for the USB subsystem to toggle the power
> > for a power cycle or to disable the port.
> >
> > Also rewrite the existing set_bit() and clear_bit() branches with
> > assign_bit() to make it cleaner.
> >
> > Signed-off-by: Chen-Yu Tsai <wenst at chromium.org>
> > ---
> > Changes since v2:
> > - Expanded subject to mention power sequencing API
> > - Dropped commit message bit about power sequencing Kconfig symbol change
> > to bool
> > - Added optional dependency on POWER_SEQUENCING to USB
> > - Split out pwrseq_power_*() calls into separate helpers
> > - Rewrote set_bit() and clear_bit() branches with assign_bit()
> > - Dropped the pwrseq_power_off() before pwrseq_put(): pwrseq_put() does it
> > automatically.
> > - Removed pwrseq_power_on() from usb_hub_create_port_device(); it will
> > get called through usb_hub_set_port_power() in hub_activate().
> > - Added checks for port->pwrseq in hub_is_port_power_switchable()
> >
> > - Use separate pwrseq descriptors for HighSpeed and SuperSpeed ports.
> > This makes things simpler. On the other hand to power cycle a port
> > userspace needs to toggle it on both the HS and SS ports together.
> > - Dropped pwrseq state tracking again
> > The power sequencing consumer API already tracks the state internally;
> > doing it again in |struct usb_port| is not necessary especially now
> > that the descriptors aren't shared.
> >
> > It's unclear to me how actual hubs reconcile USB_PORT_FEAT_POWER settings
> > from the HS side and SS side. One hub chip vendor said that VBUS_EN for
> > a port is on if the flag is set on either side; however actually testing
> > on one of their hubs showed that VBUS was cut as soon as the flag is
> > cleared on the HS port. Maybe it could be different if a SS device was
> > connected? That scenario was not tested. Testing on another retail
> > bought hub seemed to work exactly as described though: USB_PORT_FEAT_POWER
> > needed to be clear on both HS and SS ports to turn off VBUS.
> >
> > Under this scheme, I'm not sure how the power cycle in hub_port_connect()
> > would work correctly.
> >
> > - Link to v2:
> > https://lore.kernel.org/all/20260610084053.2059858-1-wenst@chromium.org/
> >
> > Changes since v1:
> > - Switch to fwnode instead of OF
> > - Tie port@ fwnode to usb_port device
> > - Move remote node compatible checking to separate helper
> > - Use usb_port device to request power sequencing descriptor
> > - Drop "index" parameter from pwrseq_get()
> > - Do not get pwrseq descriptor for SuperSpeed port; share one for one
> > physical port
> > - Add pwrseq state tracking
> > - Link to v1:
> > https://lore.kernel.org/all/20260515090149.3169406-1-wenst@chromium.org/
> > ---
> > drivers/usb/Kconfig | 1 +
> > drivers/usb/core/hub.c | 44 +++++++++++++++++++++++++++++-----
> > drivers/usb/core/hub.h | 10 +++++++-
> > drivers/usb/core/port.c | 52 ++++++++++++++++++++++++++++++++++++++++-
> > 4 files changed, 99 insertions(+), 8 deletions(-)
> >
> > diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
> > index abf8c6cdea9e..ef1959363fb1 100644
> > --- a/drivers/usb/Kconfig
> > +++ b/drivers/usb/Kconfig
> > @@ -44,6 +44,7 @@ config USB_ARCH_HAS_HCD
> > config USB
> > tristate "Support for Host-side USB"
> > depends on USB_ARCH_HAS_HCD
> > + depends on POWER_SEQUENCING if POWER_SEQUENCING
> > select GENERIC_ALLOCATOR
> > select USB_COMMON
> > select NLS # for UTF-8 strings
> > diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
> > index 8ae97e8c26aa..dfa0f5dd75e8 100644
> > --- a/drivers/usb/core/hub.c
> > +++ b/drivers/usb/core/hub.c
> > @@ -32,6 +32,7 @@
> > #include <linux/mutex.h>
> > #include <linux/random.h>
> > #include <linux/pm_qos.h>
> > +#include <linux/pwrseq/consumer.h>
> > #include <linux/kobject.h>
> >
> > #include <linux/bitfield.h>
> > @@ -871,6 +872,30 @@ static void hub_tt_work(struct work_struct *work)
> > spin_unlock_irqrestore(&hub->tt.lock, flags);
> > }
> >
> > +static int usb_hub_set_port_pwrseq(struct usb_port *port, bool set)
> > +{
> > + int ret = 0;
> > +
> > + if (set)
> > + ret = pwrseq_power_on(port->pwrseq);
> > + else
> > + ret = pwrseq_power_off(port->pwrseq);
> > +
> > + return ret;
> > +}
> > +
> > +static int usb_hub_restore_port_pwrseq(struct usb_port *port, bool set)
> > +{
> > + int ret = 0;
> > +
> > + if (set)
> > + ret = pwrseq_power_off(port->pwrseq);
> > + else
> > + ret = pwrseq_power_on(port->pwrseq);
> > +
> > + return ret;
> > +}
> > +
> > /**
> > * usb_hub_set_port_power - control hub port's power state
> > * @hdev: USB device belonging to the usb hub
> > @@ -886,20 +911,24 @@ static void hub_tt_work(struct work_struct *work)
> > int usb_hub_set_port_power(struct usb_device *hdev, struct usb_hub *hub,
> > int port1, bool set)
> > {
> > + struct usb_port *pwrseq_port = hub->ports[port1 - 1];
> > int ret;
> >
> > + ret = usb_hub_set_port_pwrseq(pwrseq_port, set);
> > + if (ret)
> > + return ret;
> > +
As Sashiko pointed out, the stub function returns -ENOSYS. I need to add
something to handle that case properly in the helper functions above.
if (!IS_ENABLED(POWER_SEQUENCING))
return 0;
seems to be more straightforward.
> > if (set)
> > ret = set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
> > else
> > ret = usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
> >
> > - if (ret)
> > + if (ret) {
> > + usb_hub_restore_port_pwrseq(pwrseq_port, set);
> > return ret;
> > + }
> >
> > - if (set)
> > - set_bit(port1, hub->power_bits);
> > - else
> > - clear_bit(port1, hub->power_bits);
> > + assign_bit(port1, hub->power_bits, set);
> > return 0;
> > }
> >
> > @@ -3249,7 +3278,10 @@ int usb_port_is_power_on(struct usb_port *port, unsigned int portstatus)
> > ret = 1;
> > }
> >
> > - return ret;
> > + if (!port->pwrseq)
> > + return ret;
> > +
> > + return ret && pwrseq_power_is_on(port->pwrseq);
>
> So this is only needed to not have to track the state in a separate field?
Correct. Reading the field is not the problem. Updating it is, as the
fields are packed bitfields, and Sashiko suggested that locking would
be needed, though it may have been for the peer port, which has since
been removed.
[...]
> Looks good to me.
>
> Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski at oss.qualcomm.com>
Thanks
ChenYu
More information about the linux-arm-kernel
mailing list