Commit b2911a59 authored by Mika Westerberg's avatar Mika Westerberg
Browse files

thunderbolt: Enable wakes from system suspend



In order for the router and the whole domain to wake up from system
suspend states we need to enable wakes for the connected routers. For
device routers we enable wakes from PCIe and USB 3.x. This allows
devices such as keyboards connected to USB 3.x hub that is tunneled to
wake the system up as expected. For all routers we enabled wake on USB4
for each connected ports. This is used to propagate the wake from router
to another.

Do the same for legacy routers through link controller vendor specific
registers as documented in USB4 spec chapter 13.

While there correct kernel-doc of usb4_switch_set_sleep() -- it does not
enable wakes instead there is a separate function (usb4_switch_set_wake())
that does.

Signed-off-by: default avatarMika Westerberg <mika.westerberg@linux.intel.com>
parent 341d4518
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -455,6 +455,8 @@ int tb_domain_add(struct tb *tb)
	/* This starts event processing */
	mutex_unlock(&tb->lock);

	device_init_wakeup(&tb->dev, true);

	pm_runtime_no_callbacks(&tb->dev);
	pm_runtime_set_active(&tb->dev);
	pm_runtime_enable(&tb->dev);
+67 −0
Original line number Diff line number Diff line
@@ -158,6 +158,73 @@ void tb_lc_unconfigure_xdomain(struct tb_port *port)
	tb_lc_set_xdomain_configured(port, false);
}

static int tb_lc_set_wake_one(struct tb_switch *sw, unsigned int offset,
			      unsigned int flags)
{
	u32 ctrl;
	int ret;

	/*
	 * Enable wake on PCIe and USB4 (wake coming from another
	 * router).
	 */
	ret = tb_sw_read(sw, &ctrl, TB_CFG_SWITCH,
			 offset + TB_LC_SX_CTRL, 1);
	if (ret)
		return ret;

	ctrl &= ~(TB_LC_SX_CTRL_WOC | TB_LC_SX_CTRL_WOD | TB_LC_SX_CTRL_WOP |
		  TB_LC_SX_CTRL_WOU4);

	if (flags & TB_WAKE_ON_CONNECT)
		ctrl |= TB_LC_SX_CTRL_WOC | TB_LC_SX_CTRL_WOD;
	if (flags & TB_WAKE_ON_USB4)
		ctrl |= TB_LC_SX_CTRL_WOU4;
	if (flags & TB_WAKE_ON_PCIE)
		ctrl |= TB_LC_SX_CTRL_WOP;

	return tb_sw_write(sw, &ctrl, TB_CFG_SWITCH, offset + TB_LC_SX_CTRL, 1);
}

/**
 * tb_lc_set_wake() - Enable/disable wake
 * @sw: Switch whose wakes to configure
 * @flags: Wakeup flags (%0 to disable)
 *
 * For each LC sets wake bits accordingly.
 */
int tb_lc_set_wake(struct tb_switch *sw, unsigned int flags)
{
	int start, size, nlc, ret, i;
	u32 desc;

	if (sw->generation < 2)
		return 0;

	if (!tb_route(sw))
		return 0;

	ret = read_lc_desc(sw, &desc);
	if (ret)
		return ret;

	/* Figure out number of link controllers */
	nlc = desc & TB_LC_DESC_NLC_MASK;
	start = (desc & TB_LC_DESC_SIZE_MASK) >> TB_LC_DESC_SIZE_SHIFT;
	size = (desc & TB_LC_DESC_PORT_SIZE_MASK) >> TB_LC_DESC_PORT_SIZE_SHIFT;

	/* For each link controller set sleep bit */
	for (i = 0; i < nlc; i++) {
		unsigned int offset = sw->cap_lc + start + i * size;

		ret = tb_lc_set_wake_one(sw, offset, flags);
		if (ret)
			return ret;
	}

	return 0;
}

/**
 * tb_lc_set_sleep() - Inform LC that the switch is going to sleep
 * @sw: Switch to set sleep
+2 −0
Original line number Diff line number Diff line
@@ -1157,6 +1157,8 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
	}
	pci_set_drvdata(pdev, tb);

	device_wakeup_enable(&pdev->dev);

	pm_runtime_allow(&pdev->dev);
	pm_runtime_set_autosuspend_delay(&pdev->dev, TB_AUTOSUSPEND_DELAY);
	pm_runtime_use_autosuspend(&pdev->dev);
+29 −1
Original line number Diff line number Diff line
@@ -2501,6 +2501,13 @@ int tb_switch_add(struct tb_switch *sw)
		return ret;
	}

	/*
	 * Thunderbolt routers do not generate wakeups themselves but
	 * they forward wakeups from tunneled protocols, so enable it
	 * here.
	 */
	device_init_wakeup(&sw->dev, true);

	pm_runtime_set_active(&sw->dev);
	if (sw->rpm) {
		pm_runtime_set_autosuspend_delay(&sw->dev, TB_AUTOSUSPEND_DELAY);
@@ -2578,6 +2585,18 @@ void tb_sw_set_unplugged(struct tb_switch *sw)
	}
}

static int tb_switch_set_wake(struct tb_switch *sw, unsigned int flags)
{
	if (flags)
		tb_sw_dbg(sw, "enabling wakeup: %#x\n", flags);
	else
		tb_sw_dbg(sw, "disabling wakeup\n");

	if (tb_switch_is_usb4(sw))
		return usb4_switch_set_wake(sw, flags);
	return tb_lc_set_wake(sw, flags);
}

int tb_switch_resume(struct tb_switch *sw)
{
	struct tb_port *port;
@@ -2623,6 +2642,9 @@ int tb_switch_resume(struct tb_switch *sw)
	if (err)
		return err;

	/* Disable wakes */
	tb_switch_set_wake(sw, 0);

	err = tb_switch_tmu_init(sw);
	if (err)
		return err;
@@ -2658,6 +2680,7 @@ int tb_switch_resume(struct tb_switch *sw)

void tb_switch_suspend(struct tb_switch *sw)
{
	unsigned int flags = 0;
	struct tb_port *port;
	int err;

@@ -2670,6 +2693,11 @@ void tb_switch_suspend(struct tb_switch *sw)
			tb_switch_suspend(port->remote->sw);
	}

	if (device_may_wakeup(&sw->dev))
		flags = TB_WAKE_ON_USB4 | TB_WAKE_ON_USB3 | TB_WAKE_ON_PCIE;

	tb_switch_set_wake(sw, flags);

	if (tb_switch_is_usb4(sw))
		usb4_switch_set_sleep(sw);
	else
+9 −0
Original line number Diff line number Diff line
@@ -333,6 +333,13 @@ struct tb_path {
 */
#define TB_PATH_MAX_HOPS	(7 * 2)

/* Possible wake types */
#define TB_WAKE_ON_CONNECT	BIT(0)
#define TB_WAKE_ON_DISCONNECT	BIT(1)
#define TB_WAKE_ON_USB4		BIT(2)
#define TB_WAKE_ON_USB3		BIT(3)
#define TB_WAKE_ON_PCIE		BIT(4)

/**
 * struct tb_cm_ops - Connection manager specific operations vector
 * @driver_ready: Called right after control channel is started. Used by
@@ -852,6 +859,7 @@ int tb_lc_configure_port(struct tb_port *port);
void tb_lc_unconfigure_port(struct tb_port *port);
int tb_lc_configure_xdomain(struct tb_port *port);
void tb_lc_unconfigure_xdomain(struct tb_port *port);
int tb_lc_set_wake(struct tb_switch *sw, unsigned int flags);
int tb_lc_set_sleep(struct tb_switch *sw);
bool tb_lc_lane_bonding_possible(struct tb_switch *sw);
bool tb_lc_dp_sink_query(struct tb_switch *sw, struct tb_port *in);
@@ -907,6 +915,7 @@ int usb4_switch_read_uid(struct tb_switch *sw, u64 *uid);
int usb4_switch_drom_read(struct tb_switch *sw, unsigned int address, void *buf,
			  size_t size);
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_sleep(struct tb_switch *sw);
int usb4_switch_nvm_sector_size(struct tb_switch *sw);
int usb4_switch_nvm_read(struct tb_switch *sw, unsigned int address, void *buf,
Loading