mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-28 13:22:57 +00:00
thunderbolt: Add wake on connect/disconnect on USB4 ports
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>
This commit is contained in:
parent
2ae18cc226
commit
a5cfc9d658
3 changed files with 30 additions and 8 deletions
|
@ -361,6 +361,8 @@ struct tb_regs_port_header {
|
||||||
#define PORT_CS_18_BE BIT(8)
|
#define PORT_CS_18_BE BIT(8)
|
||||||
#define PORT_CS_18_TCM BIT(9)
|
#define PORT_CS_18_TCM BIT(9)
|
||||||
#define PORT_CS_18_CPS BIT(10)
|
#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_18_WOU4S BIT(18)
|
||||||
#define PORT_CS_19 0x13
|
#define PORT_CS_19 0x13
|
||||||
#define PORT_CS_19_PC BIT(3)
|
#define PORT_CS_19_PC BIT(3)
|
||||||
|
|
|
@ -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)
|
static void usb4_switch_check_wakes(struct tb_switch *sw)
|
||||||
{
|
{
|
||||||
|
bool wakeup_usb4 = false;
|
||||||
|
struct usb4_port *usb4;
|
||||||
struct tb_port *port;
|
struct tb_port *port;
|
||||||
bool wakeup = false;
|
bool wakeup = false;
|
||||||
u32 val;
|
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);
|
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) {
|
tb_switch_for_each_port(sw, port) {
|
||||||
if (!tb_port_has_remote(port))
|
if (!port->cap_usb4)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (tb_port_read(port, &val, TB_CFG_PORT,
|
if (tb_port_read(port, &val, TB_CFG_PORT,
|
||||||
port->cap_usb4 + PORT_CS_18, 1))
|
port->cap_usb4 + PORT_CS_18, 1))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
tb_port_dbg(port, "USB4 wake: %s\n",
|
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_WOU4S) ? "yes" : "no",
|
||||||
|
(val & PORT_CS_18_WOCS) ? "yes" : "no",
|
||||||
|
(val & PORT_CS_18_WODS) ? "yes" : "no");
|
||||||
|
|
||||||
if (val & PORT_CS_18_WOU4S)
|
wakeup_usb4 = val & (PORT_CS_18_WOU4S | PORT_CS_18_WOCS |
|
||||||
wakeup = true;
|
PORT_CS_18_WODS);
|
||||||
|
|
||||||
|
usb4 = port->usb4;
|
||||||
|
if (device_may_wakeup(&usb4->dev) && wakeup_usb4)
|
||||||
|
pm_wakeup_event(&usb4->dev, 0);
|
||||||
|
|
||||||
|
wakeup |= wakeup_usb4;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wakeup)
|
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)
|
int usb4_switch_set_wake(struct tb_switch *sw, unsigned int flags)
|
||||||
{
|
{
|
||||||
|
struct usb4_port *usb4;
|
||||||
struct tb_port *port;
|
struct tb_port *port;
|
||||||
u64 route = tb_route(sw);
|
u64 route = tb_route(sw);
|
||||||
u32 val;
|
u32 val;
|
||||||
|
@ -395,10 +409,13 @@ int usb4_switch_set_wake(struct tb_switch *sw, unsigned int flags)
|
||||||
val |= PORT_CS_19_WOU4;
|
val |= PORT_CS_19_WOU4;
|
||||||
} else {
|
} else {
|
||||||
bool configured = val & PORT_CS_19_PC;
|
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;
|
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;
|
val |= PORT_CS_19_WOD;
|
||||||
if ((flags & TB_WAKE_ON_USB4) && configured)
|
if ((flags & TB_WAKE_ON_USB4) && configured)
|
||||||
val |= PORT_CS_19_WOU4;
|
val |= PORT_CS_19_WOU4;
|
||||||
|
|
|
@ -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_no_callbacks(&usb4->dev);
|
||||||
pm_runtime_set_active(&usb4->dev);
|
pm_runtime_set_active(&usb4->dev);
|
||||||
pm_runtime_enable(&usb4->dev);
|
pm_runtime_enable(&usb4->dev);
|
||||||
|
|
Loading…
Reference in a new issue