summaryrefslogtreecommitdiffstats
path: root/drivers/thunderbolt/tb.c
diff options
context:
space:
mode:
authorAndreas Noever <andreas.noever@gmail.com>2014-06-03 16:04:12 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-06-19 17:13:00 -0400
commit23dd5bb49d986f37977ed80dd2ca65040ead4392 (patch)
tree390db91ea55659f22ca93a15ac41bf584bd3a9b9 /drivers/thunderbolt/tb.c
parentc90553b3c4ac2389a71a5c012b6e5bb1160d48a7 (diff)
thunderbolt: Add suspend/hibernate support
We use _noirq since we have to restore the pci tunnels before the pci core wakes the tunneled devices. Signed-off-by: Andreas Noever <andreas.noever@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/thunderbolt/tb.c')
-rw-r--r--drivers/thunderbolt/tb.c61
1 files changed, 61 insertions, 0 deletions
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index 177f61df464d..1aa6dd7dc68b 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -69,6 +69,28 @@ static void tb_free_invalid_tunnels(struct tb *tb)
69} 69}
70 70
71/** 71/**
72 * tb_free_unplugged_children() - traverse hierarchy and free unplugged switches
73 */
74static void tb_free_unplugged_children(struct tb_switch *sw)
75{
76 int i;
77 for (i = 1; i <= sw->config.max_port_number; i++) {
78 struct tb_port *port = &sw->ports[i];
79 if (tb_is_upstream_port(port))
80 continue;
81 if (!port->remote)
82 continue;
83 if (port->remote->sw->is_unplugged) {
84 tb_switch_free(port->remote->sw);
85 port->remote = NULL;
86 } else {
87 tb_free_unplugged_children(port->remote->sw);
88 }
89 }
90}
91
92
93/**
72 * find_pci_up_port() - return the first PCIe up port on @sw or NULL 94 * find_pci_up_port() - return the first PCIe up port on @sw or NULL
73 */ 95 */
74static struct tb_port *tb_find_pci_up_port(struct tb_switch *sw) 96static struct tb_port *tb_find_pci_up_port(struct tb_switch *sw)
@@ -368,3 +390,42 @@ err_locked:
368 return NULL; 390 return NULL;
369} 391}
370 392
393void thunderbolt_suspend(struct tb *tb)
394{
395 tb_info(tb, "suspending...\n");
396 mutex_lock(&tb->lock);
397 tb_switch_suspend(tb->root_switch);
398 tb_ctl_stop(tb->ctl);
399 tb->hotplug_active = false; /* signal tb_handle_hotplug to quit */
400 mutex_unlock(&tb->lock);
401 tb_info(tb, "suspend finished\n");
402}
403
404void thunderbolt_resume(struct tb *tb)
405{
406 struct tb_pci_tunnel *tunnel, *n;
407 tb_info(tb, "resuming...\n");
408 mutex_lock(&tb->lock);
409 tb_ctl_start(tb->ctl);
410
411 /* remove any pci devices the firmware might have setup */
412 tb_switch_reset(tb, 0);
413
414 tb_switch_resume(tb->root_switch);
415 tb_free_invalid_tunnels(tb);
416 tb_free_unplugged_children(tb->root_switch);
417 list_for_each_entry_safe(tunnel, n, &tb->tunnel_list, list)
418 tb_pci_restart(tunnel);
419 if (!list_empty(&tb->tunnel_list)) {
420 /*
421 * the pcie links need some time to get going.
422 * 100ms works for me...
423 */
424 tb_info(tb, "tunnels restarted, sleeping for 100ms\n");
425 msleep(100);
426 }
427 /* Allow tb_handle_hotplug to progress events */
428 tb->hotplug_active = true;
429 mutex_unlock(&tb->lock);
430 tb_info(tb, "resume finished\n");
431}