diff options
author | Andreas Noever <andreas.noever@gmail.com> | 2014-06-03 22:04:12 +0200 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-06-19 14:13:00 -0700 |
commit | 23dd5bb49d986f37977ed80dd2ca65040ead4392 (patch) | |
tree | 390db91ea55659f22ca93a15ac41bf584bd3a9b9 /drivers/thunderbolt/tb.c | |
parent | c90553b3c4ac2389a71a5c012b6e5bb1160d48a7 (diff) | |
download | op-kernel-dev-23dd5bb49d986f37977ed80dd2ca65040ead4392.zip op-kernel-dev-23dd5bb49d986f37977ed80dd2ca65040ead4392.tar.gz |
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.c | 61 |
1 files changed, 61 insertions, 0 deletions
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 177f61d..1aa6dd7 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -69,6 +69,28 @@ static void tb_free_invalid_tunnels(struct tb *tb) } /** + * tb_free_unplugged_children() - traverse hierarchy and free unplugged switches + */ +static void tb_free_unplugged_children(struct tb_switch *sw) +{ + int i; + for (i = 1; i <= sw->config.max_port_number; i++) { + struct tb_port *port = &sw->ports[i]; + if (tb_is_upstream_port(port)) + continue; + if (!port->remote) + continue; + if (port->remote->sw->is_unplugged) { + tb_switch_free(port->remote->sw); + port->remote = NULL; + } else { + tb_free_unplugged_children(port->remote->sw); + } + } +} + + +/** * find_pci_up_port() - return the first PCIe up port on @sw or NULL */ static struct tb_port *tb_find_pci_up_port(struct tb_switch *sw) @@ -368,3 +390,42 @@ err_locked: return NULL; } +void thunderbolt_suspend(struct tb *tb) +{ + tb_info(tb, "suspending...\n"); + mutex_lock(&tb->lock); + tb_switch_suspend(tb->root_switch); + tb_ctl_stop(tb->ctl); + tb->hotplug_active = false; /* signal tb_handle_hotplug to quit */ + mutex_unlock(&tb->lock); + tb_info(tb, "suspend finished\n"); +} + +void thunderbolt_resume(struct tb *tb) +{ + struct tb_pci_tunnel *tunnel, *n; + tb_info(tb, "resuming...\n"); + mutex_lock(&tb->lock); + tb_ctl_start(tb->ctl); + + /* remove any pci devices the firmware might have setup */ + tb_switch_reset(tb, 0); + + tb_switch_resume(tb->root_switch); + tb_free_invalid_tunnels(tb); + tb_free_unplugged_children(tb->root_switch); + list_for_each_entry_safe(tunnel, n, &tb->tunnel_list, list) + tb_pci_restart(tunnel); + if (!list_empty(&tb->tunnel_list)) { + /* + * the pcie links need some time to get going. + * 100ms works for me... + */ + tb_info(tb, "tunnels restarted, sleeping for 100ms\n"); + msleep(100); + } + /* Allow tb_handle_hotplug to progress events */ + tb->hotplug_active = true; + mutex_unlock(&tb->lock); + tb_info(tb, "resume finished\n"); +} |