]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
usb: xhci: rework xhci_decode_portsc()
authorNiklas Neronin <niklas.neronin@linux.intel.com>
Wed, 19 Nov 2025 14:23:58 +0000 (16:23 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 21 Nov 2025 13:52:59 +0000 (14:52 +0100)
Rework xhci_decode_portsc(), which is used for PORTSC tracing, to make
the output more compact and general.

The function now first prints the multi-bit fields (port speed and link
state), followed by the abbreviated names of each individual bit as
defined in the xHCI specification. This reduces message length and makes
the output easier to read.

This change prepares for upcoming patches that will trace all PORTSC
writes, requiring the same decoding logic to handle both reads and writes.
This is particularly important for Read-Write-1-to-Clear (RW1C) bits,
where the semantics differ between read and write operations. For
example, when reading the Port Enabled bit, a set bit means the port is
enabled; when writing, a set bit indicates the port is being disabled.

The decoder now also includes the following fields:
 Port Link State Write Strobe (LWS)
 Device Removable (DR)
 Warm Port Reset (WPR)

==== Examples Traces ====

Before:
0x00201201 Powered Connected Disabled Link:U0 PortSpeed:4 Change: PRC Wake:
0x0a0002a0 Powered Not-connected Disabled Link:RxDetect PortSpeed:0 \
 Change: Wake: WCE WOE

After:
0x00201201 Speed=4 Link=U0 CCS PP PRC
0x0a0002a0 Speed=0 Link=RxDetect PP WCE WOE

Signed-off-by: Niklas Neronin <niklas.neronin@linux.intel.com>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
Link: https://patch.msgid.link/20251119142417.2820519-5-mathias.nyman@linux.intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/host/xhci.h

index 58a51f09cceb8f0c11c9f94a5744a133c6ee03ad..8e1311f90fdbebde05906d11061a9c3749dd25d6 100644 (file)
@@ -2399,25 +2399,48 @@ static inline const char *xhci_decode_portsc(char *str, u32 portsc)
        if (portsc == ~(u32)0)
                return str;
 
-       ret += sprintf(str + ret, "%s %s %s Link:%s PortSpeed:%d ",
-                     portsc & PORT_POWER       ? "Powered" : "Powered-off",
-                     portsc & PORT_CONNECT     ? "Connected" : "Not-connected",
-                     portsc & PORT_PE          ? "Enabled" : "Disabled",
-                     xhci_portsc_link_state_string(portsc),
-                     DEV_PORT_SPEED(portsc));
+       ret += sprintf(str + ret, "Speed=%d ", DEV_PORT_SPEED(portsc));
+       ret += sprintf(str + ret, "Link=%s ", xhci_portsc_link_state_string(portsc));
 
+       /* RO/ROS: Read-only */
+       if (portsc & PORT_CONNECT)
+               ret += sprintf(str + ret, "CCS ");
        if (portsc & PORT_OC)
-               ret += sprintf(str + ret, "OverCurrent ");
+               ret += sprintf(str + ret, "OCA "); /* No set for USB2 ports */
+       if (portsc & PORT_CAS)
+               ret += sprintf(str + ret, "CAS ");
+       if (portsc & PORT_DEV_REMOVE)
+               ret += sprintf(str + ret, "DR ");
+
+       /* RWS; writing 1 sets the bit, writing 0 clears the bit. */
+       if (portsc & PORT_POWER)
+               ret += sprintf(str + ret, "PP ");
+       if (portsc & PORT_WKCONN_E)
+               ret += sprintf(str + ret, "WCE ");
+       if (portsc & PORT_WKDISC_E)
+               ret += sprintf(str + ret, "WDE ");
+       if (portsc & PORT_WKOC_E)
+               ret += sprintf(str + ret, "WOE ");
+
+       /* RW; writing 1 sets the bit, writing 0 clears the bit */
+       if (portsc & PORT_LINK_STROBE)
+               ret += sprintf(str + ret, "LWS "); /* LWS 0 write is ignored */
+
+       /* RW1S; writing 1 sets the bit, writing 0 has no effect */
        if (portsc & PORT_RESET)
-               ret += sprintf(str + ret, "In-Reset ");
+               ret += sprintf(str + ret, "PR ");
+       if (portsc & PORT_WR)
+               ret += sprintf(str + ret, "WPR "); /* RsvdZ for USB2 ports */
 
-       ret += sprintf(str + ret, "Change: ");
+       /* RW1CS; writing 1 clears the bit, writing 0 has no effect. */
+       if (portsc & PORT_PE)
+               ret += sprintf(str + ret, "PED ");
        if (portsc & PORT_CSC)
                ret += sprintf(str + ret, "CSC ");
        if (portsc & PORT_PEC)
-               ret += sprintf(str + ret, "PEC ");
+               ret += sprintf(str + ret, "PEC "); /* No set for USB3 ports */
        if (portsc & PORT_WRC)
-               ret += sprintf(str + ret, "WRC ");
+               ret += sprintf(str + ret, "WRC "); /* RsvdZ for USB2 ports */
        if (portsc & PORT_OCC)
                ret += sprintf(str + ret, "OCC ");
        if (portsc & PORT_RC)
@@ -2425,17 +2448,7 @@ static inline const char *xhci_decode_portsc(char *str, u32 portsc)
        if (portsc & PORT_PLC)
                ret += sprintf(str + ret, "PLC ");
        if (portsc & PORT_CEC)
-               ret += sprintf(str + ret, "CEC ");
-       if (portsc & PORT_CAS)
-               ret += sprintf(str + ret, "CAS ");
-
-       ret += sprintf(str + ret, "Wake: ");
-       if (portsc & PORT_WKCONN_E)
-               ret += sprintf(str + ret, "WCE ");
-       if (portsc & PORT_WKDISC_E)
-               ret += sprintf(str + ret, "WDE ");
-       if (portsc & PORT_WKOC_E)
-               ret += sprintf(str + ret, "WOE ");
+               ret += sprintf(str + ret, "CEC "); /* RsvdZ for USB2 ports */
 
        return str;
 }