thunderbolt: Add wake on connect/disconnect on USB4 ports
authorRajat Khandelwal <rajat.khandelwal@intel.com>
Tue, 1 Nov 2022 11:50:42 +0000 (17:20 +0530)
committerMika Westerberg <mika.westerberg@linux.intel.com>
Mon, 7 Nov 2022 12:36:47 +0000 (14:36 +0200)
Wake on connect/disconnect is only supported while runtime suspend for
now, which is obviously necessary. It is also not inherently desired for
the system to wakeup on Thunderbolt/USB4 hot plug events. However, we
can still make user in control of waking up the system in the events of
hot plug/unplug.

This patch adds 'wakeup' attribute under 'usb4_portX/power' sysfs
attribute and only enables wakes on connect/disconnect to the respective
port when 'wakeup' is set to 'enabled'. The attribute is set to
'disabled' by default.

Signed-off-by: Rajat Khandelwal <rajat.khandelwal@intel.com>
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
drivers/thunderbolt/tb_regs.h
drivers/thunderbolt/usb4.c
drivers/thunderbolt/usb4_port.c

index 86319dca0f8ca90585879dbd6e7b3f4c489f85d0..3c38b0cb8f74eb9ebe905cf4b8d91f2e6606bb01 100644 (file)
@@ -361,6 +361,8 @@ struct tb_regs_port_header {
 #define PORT_CS_18_BE                          BIT(8)
 #define PORT_CS_18_TCM                         BIT(9)
 #define PORT_CS_18_CPS                         BIT(10)
+#define PORT_CS_18_WOCS                                BIT(16)
+#define PORT_CS_18_WODS                                BIT(17)
 #define PORT_CS_18_WOU4S                       BIT(18)
 #define PORT_CS_19                             0x13
 #define PORT_CS_19_PC                          BIT(3)
index f986854aa207ade99a87afa2e205269d15176e0d..2ed50fcbcca7a91fc85ea6789d5430c079898735 100644 (file)
@@ -155,6 +155,8 @@ static inline int usb4_switch_op_data(struct tb_switch *sw, u16 opcode,
 
 static void usb4_switch_check_wakes(struct tb_switch *sw)
 {
+       bool wakeup_usb4 = false;
+       struct usb4_port *usb4;
        struct tb_port *port;
        bool wakeup = false;
        u32 val;
@@ -173,20 +175,31 @@ static void usb4_switch_check_wakes(struct tb_switch *sw)
                wakeup = val & (ROUTER_CS_6_WOPS | ROUTER_CS_6_WOUS);
        }
 
-       /* Check for any connected downstream ports for USB4 wake */
+       /*
+        * Check for any downstream ports for USB4 wake,
+        * connection wake and disconnection wake.
+        */
        tb_switch_for_each_port(sw, port) {
-               if (!tb_port_has_remote(port))
+               if (!port->cap_usb4)
                        continue;
 
                if (tb_port_read(port, &val, TB_CFG_PORT,
                                 port->cap_usb4 + PORT_CS_18, 1))
                        break;
 
-               tb_port_dbg(port, "USB4 wake: %s\n",
-                           (val & PORT_CS_18_WOU4S) ? "yes" : "no");
+               tb_port_dbg(port, "USB4 wake: %s, connection wake: %s, disconnection wake: %s\n",
+                           (val & PORT_CS_18_WOU4S) ? "yes" : "no",
+                           (val & PORT_CS_18_WOCS) ? "yes" : "no",
+                           (val & PORT_CS_18_WODS) ? "yes" : "no");
+
+               wakeup_usb4 = val & (PORT_CS_18_WOU4S | PORT_CS_18_WOCS |
+                                    PORT_CS_18_WODS);
+
+               usb4 = port->usb4;
+               if (device_may_wakeup(&usb4->dev) && wakeup_usb4)
+                       pm_wakeup_event(&usb4->dev, 0);
 
-               if (val & PORT_CS_18_WOU4S)
-                       wakeup = true;
+               wakeup |= wakeup_usb4;
        }
 
        if (wakeup)
@@ -366,6 +379,7 @@ bool usb4_switch_lane_bonding_possible(struct tb_switch *sw)
  */
 int usb4_switch_set_wake(struct tb_switch *sw, unsigned int flags)
 {
+       struct usb4_port *usb4;
        struct tb_port *port;
        u64 route = tb_route(sw);
        u32 val;
@@ -395,10 +409,13 @@ int usb4_switch_set_wake(struct tb_switch *sw, unsigned int flags)
                        val |= PORT_CS_19_WOU4;
                } else {
                        bool configured = val & PORT_CS_19_PC;
+                       usb4 = port->usb4;
 
-                       if ((flags & TB_WAKE_ON_CONNECT) && !configured)
+                       if (((flags & TB_WAKE_ON_CONNECT) |
+                             device_may_wakeup(&usb4->dev)) && !configured)
                                val |= PORT_CS_19_WOC;
-                       if ((flags & TB_WAKE_ON_DISCONNECT) && configured)
+                       if (((flags & TB_WAKE_ON_DISCONNECT) |
+                             device_may_wakeup(&usb4->dev)) && configured)
                                val |= PORT_CS_19_WOD;
                        if ((flags & TB_WAKE_ON_USB4) && configured)
                                val |= PORT_CS_19_WOU4;
index 1a30c0a2328655cc8669ccbc062e05f6152f8c5d..e355bfd6343ff0d0616d1917fbcef9acf123023d 100644 (file)
@@ -284,6 +284,9 @@ struct usb4_port *usb4_port_device_add(struct tb_port *port)
                }
        }
 
+       if (!tb_is_upstream_port(port))
+               device_set_wakeup_capable(&usb4->dev, true);
+
        pm_runtime_no_callbacks(&usb4->dev);
        pm_runtime_set_active(&usb4->dev);
        pm_runtime_enable(&usb4->dev);