1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 2d6cc51cdSAndreas Noever /* 3d6cc51cdSAndreas Noever * Thunderbolt Cactus Ridge driver - bus logic (NHI independent) 4d6cc51cdSAndreas Noever * 5d6cc51cdSAndreas Noever * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com> 6d6cc51cdSAndreas Noever */ 7d6cc51cdSAndreas Noever 8d6cc51cdSAndreas Noever #include <linux/slab.h> 9d6cc51cdSAndreas Noever #include <linux/errno.h> 10d6cc51cdSAndreas Noever #include <linux/delay.h> 11630b3affSLukas Wunner #include <linux/platform_data/x86/apple.h> 12d6cc51cdSAndreas Noever 13d6cc51cdSAndreas Noever #include "tb.h" 147adf6097SAndreas Noever #include "tb_regs.h" 151752b9f7SMika Westerberg #include "tunnel.h" 16d6cc51cdSAndreas Noever 179d3cce0bSMika Westerberg /** 189d3cce0bSMika Westerberg * struct tb_cm - Simple Thunderbolt connection manager 199d3cce0bSMika Westerberg * @tunnel_list: List of active tunnels 209d3cce0bSMika Westerberg * @hotplug_active: tb_handle_hotplug will stop progressing plug 219d3cce0bSMika Westerberg * events and exit if this is not set (it needs to 229d3cce0bSMika Westerberg * acquire the lock one more time). Used to drain wq 239d3cce0bSMika Westerberg * after cfg has been paused. 249d3cce0bSMika Westerberg */ 259d3cce0bSMika Westerberg struct tb_cm { 269d3cce0bSMika Westerberg struct list_head tunnel_list; 279d3cce0bSMika Westerberg bool hotplug_active; 289d3cce0bSMika Westerberg }; 299da672a4SAndreas Noever 309da672a4SAndreas Noever /* enumeration & hot plug handling */ 319da672a4SAndreas Noever 320414bec5SMika Westerberg static void tb_discover_tunnels(struct tb_switch *sw) 330414bec5SMika Westerberg { 340414bec5SMika Westerberg struct tb *tb = sw->tb; 350414bec5SMika Westerberg struct tb_cm *tcm = tb_priv(tb); 360414bec5SMika Westerberg struct tb_port *port; 370414bec5SMika Westerberg int i; 380414bec5SMika Westerberg 390414bec5SMika Westerberg for (i = 1; i <= sw->config.max_port_number; i++) { 400414bec5SMika Westerberg struct tb_tunnel *tunnel = NULL; 410414bec5SMika Westerberg 420414bec5SMika Westerberg port = &sw->ports[i]; 430414bec5SMika Westerberg switch (port->config.type) { 440414bec5SMika Westerberg case TB_TYPE_PCIE_DOWN: 450414bec5SMika Westerberg tunnel = tb_tunnel_discover_pci(tb, port); 460414bec5SMika Westerberg break; 470414bec5SMika Westerberg 480414bec5SMika Westerberg default: 490414bec5SMika Westerberg break; 500414bec5SMika Westerberg } 510414bec5SMika Westerberg 520414bec5SMika Westerberg if (tunnel) { 530414bec5SMika Westerberg struct tb_switch *parent = tunnel->dst_port->sw; 540414bec5SMika Westerberg 550414bec5SMika Westerberg while (parent != tunnel->src_port->sw) { 560414bec5SMika Westerberg parent->boot = true; 570414bec5SMika Westerberg parent = tb_switch_parent(parent); 580414bec5SMika Westerberg } 590414bec5SMika Westerberg 600414bec5SMika Westerberg list_add_tail(&tunnel->list, &tcm->tunnel_list); 610414bec5SMika Westerberg } 620414bec5SMika Westerberg } 630414bec5SMika Westerberg 640414bec5SMika Westerberg for (i = 1; i <= sw->config.max_port_number; i++) { 650414bec5SMika Westerberg if (tb_port_has_remote(&sw->ports[i])) 660414bec5SMika Westerberg tb_discover_tunnels(sw->ports[i].remote->sw); 670414bec5SMika Westerberg } 680414bec5SMika Westerberg } 699da672a4SAndreas Noever 709da672a4SAndreas Noever static void tb_scan_port(struct tb_port *port); 719da672a4SAndreas Noever 729da672a4SAndreas Noever /** 739da672a4SAndreas Noever * tb_scan_switch() - scan for and initialize downstream switches 749da672a4SAndreas Noever */ 759da672a4SAndreas Noever static void tb_scan_switch(struct tb_switch *sw) 769da672a4SAndreas Noever { 779da672a4SAndreas Noever int i; 789da672a4SAndreas Noever for (i = 1; i <= sw->config.max_port_number; i++) 799da672a4SAndreas Noever tb_scan_port(&sw->ports[i]); 809da672a4SAndreas Noever } 819da672a4SAndreas Noever 829da672a4SAndreas Noever /** 839da672a4SAndreas Noever * tb_scan_port() - check for and initialize switches below port 849da672a4SAndreas Noever */ 859da672a4SAndreas Noever static void tb_scan_port(struct tb_port *port) 869da672a4SAndreas Noever { 87dfe40ca4SMika Westerberg struct tb_port *upstream_port; 889da672a4SAndreas Noever struct tb_switch *sw; 89dfe40ca4SMika Westerberg 909da672a4SAndreas Noever if (tb_is_upstream_port(port)) 919da672a4SAndreas Noever return; 929da672a4SAndreas Noever if (port->config.type != TB_TYPE_PORT) 939da672a4SAndreas Noever return; 94343fcb8cSAndreas Noever if (port->dual_link_port && port->link_nr) 95343fcb8cSAndreas Noever return; /* 96343fcb8cSAndreas Noever * Downstream switch is reachable through two ports. 97343fcb8cSAndreas Noever * Only scan on the primary port (link_nr == 0). 98343fcb8cSAndreas Noever */ 999da672a4SAndreas Noever if (tb_wait_for_port(port, false) <= 0) 1009da672a4SAndreas Noever return; 1019da672a4SAndreas Noever if (port->remote) { 1029da672a4SAndreas Noever tb_port_WARN(port, "port already has a remote!\n"); 1039da672a4SAndreas Noever return; 1049da672a4SAndreas Noever } 105bfe778acSMika Westerberg sw = tb_switch_alloc(port->sw->tb, &port->sw->dev, 106bfe778acSMika Westerberg tb_downstream_route(port)); 1079da672a4SAndreas Noever if (!sw) 1089da672a4SAndreas Noever return; 109bfe778acSMika Westerberg 110bfe778acSMika Westerberg if (tb_switch_configure(sw)) { 111bfe778acSMika Westerberg tb_switch_put(sw); 112bfe778acSMika Westerberg return; 113bfe778acSMika Westerberg } 114bfe778acSMika Westerberg 115f67cf491SMika Westerberg sw->authorized = true; 116f67cf491SMika Westerberg 117bfe778acSMika Westerberg if (tb_switch_add(sw)) { 118bfe778acSMika Westerberg tb_switch_put(sw); 119bfe778acSMika Westerberg return; 120bfe778acSMika Westerberg } 121bfe778acSMika Westerberg 122dfe40ca4SMika Westerberg /* Link the switches using both links if available */ 123dfe40ca4SMika Westerberg upstream_port = tb_upstream_port(sw); 124dfe40ca4SMika Westerberg port->remote = upstream_port; 125dfe40ca4SMika Westerberg upstream_port->remote = port; 126dfe40ca4SMika Westerberg if (port->dual_link_port && upstream_port->dual_link_port) { 127dfe40ca4SMika Westerberg port->dual_link_port->remote = upstream_port->dual_link_port; 128dfe40ca4SMika Westerberg upstream_port->dual_link_port->remote = port->dual_link_port; 129dfe40ca4SMika Westerberg } 130dfe40ca4SMika Westerberg 1319da672a4SAndreas Noever tb_scan_switch(sw); 1329da672a4SAndreas Noever } 1339da672a4SAndreas Noever 1343364f0c1SAndreas Noever /** 1353364f0c1SAndreas Noever * tb_free_invalid_tunnels() - destroy tunnels of devices that have gone away 1363364f0c1SAndreas Noever */ 1373364f0c1SAndreas Noever static void tb_free_invalid_tunnels(struct tb *tb) 1383364f0c1SAndreas Noever { 1399d3cce0bSMika Westerberg struct tb_cm *tcm = tb_priv(tb); 14093f36adeSMika Westerberg struct tb_tunnel *tunnel; 14193f36adeSMika Westerberg struct tb_tunnel *n; 1429d3cce0bSMika Westerberg 1439d3cce0bSMika Westerberg list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list) { 14493f36adeSMika Westerberg if (tb_tunnel_is_invalid(tunnel)) { 14593f36adeSMika Westerberg tb_tunnel_deactivate(tunnel); 1469d3cce0bSMika Westerberg list_del(&tunnel->list); 14793f36adeSMika Westerberg tb_tunnel_free(tunnel); 1483364f0c1SAndreas Noever } 1493364f0c1SAndreas Noever } 1503364f0c1SAndreas Noever } 1513364f0c1SAndreas Noever 1523364f0c1SAndreas Noever /** 15323dd5bb4SAndreas Noever * tb_free_unplugged_children() - traverse hierarchy and free unplugged switches 15423dd5bb4SAndreas Noever */ 15523dd5bb4SAndreas Noever static void tb_free_unplugged_children(struct tb_switch *sw) 15623dd5bb4SAndreas Noever { 15723dd5bb4SAndreas Noever int i; 15823dd5bb4SAndreas Noever for (i = 1; i <= sw->config.max_port_number; i++) { 15923dd5bb4SAndreas Noever struct tb_port *port = &sw->ports[i]; 160dfe40ca4SMika Westerberg 161dfe40ca4SMika Westerberg if (!tb_port_has_remote(port)) 16223dd5bb4SAndreas Noever continue; 163dfe40ca4SMika Westerberg 16423dd5bb4SAndreas Noever if (port->remote->sw->is_unplugged) { 165bfe778acSMika Westerberg tb_switch_remove(port->remote->sw); 16623dd5bb4SAndreas Noever port->remote = NULL; 167dfe40ca4SMika Westerberg if (port->dual_link_port) 168dfe40ca4SMika Westerberg port->dual_link_port->remote = NULL; 16923dd5bb4SAndreas Noever } else { 17023dd5bb4SAndreas Noever tb_free_unplugged_children(port->remote->sw); 17123dd5bb4SAndreas Noever } 17223dd5bb4SAndreas Noever } 17323dd5bb4SAndreas Noever } 17423dd5bb4SAndreas Noever 17523dd5bb4SAndreas Noever 17623dd5bb4SAndreas Noever /** 1773364f0c1SAndreas Noever * find_pci_up_port() - return the first PCIe up port on @sw or NULL 1783364f0c1SAndreas Noever */ 1793364f0c1SAndreas Noever static struct tb_port *tb_find_pci_up_port(struct tb_switch *sw) 1803364f0c1SAndreas Noever { 1813364f0c1SAndreas Noever int i; 1823364f0c1SAndreas Noever for (i = 1; i <= sw->config.max_port_number; i++) 1833364f0c1SAndreas Noever if (sw->ports[i].config.type == TB_TYPE_PCIE_UP) 1843364f0c1SAndreas Noever return &sw->ports[i]; 1853364f0c1SAndreas Noever return NULL; 1863364f0c1SAndreas Noever } 1873364f0c1SAndreas Noever 1883364f0c1SAndreas Noever /** 1893364f0c1SAndreas Noever * find_unused_down_port() - return the first inactive PCIe down port on @sw 1903364f0c1SAndreas Noever */ 1913364f0c1SAndreas Noever static struct tb_port *tb_find_unused_down_port(struct tb_switch *sw) 1923364f0c1SAndreas Noever { 1933364f0c1SAndreas Noever int i; 1943364f0c1SAndreas Noever int cap; 1953364f0c1SAndreas Noever int res; 1963364f0c1SAndreas Noever int data; 1973364f0c1SAndreas Noever for (i = 1; i <= sw->config.max_port_number; i++) { 1983364f0c1SAndreas Noever if (tb_is_upstream_port(&sw->ports[i])) 1993364f0c1SAndreas Noever continue; 2003364f0c1SAndreas Noever if (sw->ports[i].config.type != TB_TYPE_PCIE_DOWN) 2013364f0c1SAndreas Noever continue; 20256183c88SMika Westerberg cap = sw->ports[i].cap_adap; 20356183c88SMika Westerberg if (!cap) 2043364f0c1SAndreas Noever continue; 2053364f0c1SAndreas Noever res = tb_port_read(&sw->ports[i], &data, TB_CFG_PORT, cap, 1); 2063364f0c1SAndreas Noever if (res < 0) 2073364f0c1SAndreas Noever continue; 2083364f0c1SAndreas Noever if (data & 0x80000000) 2093364f0c1SAndreas Noever continue; 2103364f0c1SAndreas Noever return &sw->ports[i]; 2113364f0c1SAndreas Noever } 2123364f0c1SAndreas Noever return NULL; 2133364f0c1SAndreas Noever } 2143364f0c1SAndreas Noever 2153364f0c1SAndreas Noever /** 2163364f0c1SAndreas Noever * tb_activate_pcie_devices() - scan for and activate PCIe devices 2173364f0c1SAndreas Noever * 2183364f0c1SAndreas Noever * This method is somewhat ad hoc. For now it only supports one device 2193364f0c1SAndreas Noever * per port and only devices at depth 1. 2203364f0c1SAndreas Noever */ 2213364f0c1SAndreas Noever static void tb_activate_pcie_devices(struct tb *tb) 2223364f0c1SAndreas Noever { 2233364f0c1SAndreas Noever int i; 2243364f0c1SAndreas Noever int cap; 2253364f0c1SAndreas Noever u32 data; 2263364f0c1SAndreas Noever struct tb_switch *sw; 2273364f0c1SAndreas Noever struct tb_port *up_port; 2283364f0c1SAndreas Noever struct tb_port *down_port; 22993f36adeSMika Westerberg struct tb_tunnel *tunnel; 2309d3cce0bSMika Westerberg struct tb_cm *tcm = tb_priv(tb); 2319d3cce0bSMika Westerberg 2323364f0c1SAndreas Noever /* scan for pcie devices at depth 1*/ 2333364f0c1SAndreas Noever for (i = 1; i <= tb->root_switch->config.max_port_number; i++) { 2343364f0c1SAndreas Noever if (tb_is_upstream_port(&tb->root_switch->ports[i])) 2353364f0c1SAndreas Noever continue; 2363364f0c1SAndreas Noever if (tb->root_switch->ports[i].config.type != TB_TYPE_PORT) 2373364f0c1SAndreas Noever continue; 2383364f0c1SAndreas Noever if (!tb->root_switch->ports[i].remote) 2393364f0c1SAndreas Noever continue; 2403364f0c1SAndreas Noever sw = tb->root_switch->ports[i].remote->sw; 2413364f0c1SAndreas Noever up_port = tb_find_pci_up_port(sw); 2423364f0c1SAndreas Noever if (!up_port) { 2433364f0c1SAndreas Noever tb_sw_info(sw, "no PCIe devices found, aborting\n"); 2443364f0c1SAndreas Noever continue; 2453364f0c1SAndreas Noever } 2463364f0c1SAndreas Noever 2473364f0c1SAndreas Noever /* check whether port is already activated */ 24856183c88SMika Westerberg cap = up_port->cap_adap; 24956183c88SMika Westerberg if (!cap) 2503364f0c1SAndreas Noever continue; 2513364f0c1SAndreas Noever if (tb_port_read(up_port, &data, TB_CFG_PORT, cap, 1)) 2523364f0c1SAndreas Noever continue; 2533364f0c1SAndreas Noever if (data & 0x80000000) { 2543364f0c1SAndreas Noever tb_port_info(up_port, 2553364f0c1SAndreas Noever "PCIe port already activated, aborting\n"); 2563364f0c1SAndreas Noever continue; 2573364f0c1SAndreas Noever } 2583364f0c1SAndreas Noever 2593364f0c1SAndreas Noever down_port = tb_find_unused_down_port(tb->root_switch); 2603364f0c1SAndreas Noever if (!down_port) { 2613364f0c1SAndreas Noever tb_port_info(up_port, 2623364f0c1SAndreas Noever "All PCIe down ports are occupied, aborting\n"); 2633364f0c1SAndreas Noever continue; 2643364f0c1SAndreas Noever } 26593f36adeSMika Westerberg tunnel = tb_tunnel_alloc_pci(tb, up_port, down_port); 2663364f0c1SAndreas Noever if (!tunnel) { 2673364f0c1SAndreas Noever tb_port_info(up_port, 2683364f0c1SAndreas Noever "PCIe tunnel allocation failed, aborting\n"); 2693364f0c1SAndreas Noever continue; 2703364f0c1SAndreas Noever } 2713364f0c1SAndreas Noever 27293f36adeSMika Westerberg if (tb_tunnel_activate(tunnel)) { 2733364f0c1SAndreas Noever tb_port_info(up_port, 2743364f0c1SAndreas Noever "PCIe tunnel activation failed, aborting\n"); 27593f36adeSMika Westerberg tb_tunnel_free(tunnel); 276a2e37343SGustavo A. R. Silva continue; 2773364f0c1SAndreas Noever } 2783364f0c1SAndreas Noever 2799d3cce0bSMika Westerberg list_add(&tunnel->list, &tcm->tunnel_list); 2803364f0c1SAndreas Noever } 2813364f0c1SAndreas Noever } 2829da672a4SAndreas Noever 283d6cc51cdSAndreas Noever /* hotplug handling */ 284d6cc51cdSAndreas Noever 285d6cc51cdSAndreas Noever struct tb_hotplug_event { 286d6cc51cdSAndreas Noever struct work_struct work; 287d6cc51cdSAndreas Noever struct tb *tb; 288d6cc51cdSAndreas Noever u64 route; 289d6cc51cdSAndreas Noever u8 port; 290d6cc51cdSAndreas Noever bool unplug; 291d6cc51cdSAndreas Noever }; 292d6cc51cdSAndreas Noever 293d6cc51cdSAndreas Noever /** 294d6cc51cdSAndreas Noever * tb_handle_hotplug() - handle hotplug event 295d6cc51cdSAndreas Noever * 296d6cc51cdSAndreas Noever * Executes on tb->wq. 297d6cc51cdSAndreas Noever */ 298d6cc51cdSAndreas Noever static void tb_handle_hotplug(struct work_struct *work) 299d6cc51cdSAndreas Noever { 300d6cc51cdSAndreas Noever struct tb_hotplug_event *ev = container_of(work, typeof(*ev), work); 301d6cc51cdSAndreas Noever struct tb *tb = ev->tb; 3029d3cce0bSMika Westerberg struct tb_cm *tcm = tb_priv(tb); 303053596d9SAndreas Noever struct tb_switch *sw; 304053596d9SAndreas Noever struct tb_port *port; 305d6cc51cdSAndreas Noever mutex_lock(&tb->lock); 3069d3cce0bSMika Westerberg if (!tcm->hotplug_active) 307d6cc51cdSAndreas Noever goto out; /* during init, suspend or shutdown */ 308d6cc51cdSAndreas Noever 3098f965efdSMika Westerberg sw = tb_switch_find_by_route(tb, ev->route); 310053596d9SAndreas Noever if (!sw) { 311053596d9SAndreas Noever tb_warn(tb, 312053596d9SAndreas Noever "hotplug event from non existent switch %llx:%x (unplug: %d)\n", 313053596d9SAndreas Noever ev->route, ev->port, ev->unplug); 314053596d9SAndreas Noever goto out; 315053596d9SAndreas Noever } 316053596d9SAndreas Noever if (ev->port > sw->config.max_port_number) { 317053596d9SAndreas Noever tb_warn(tb, 318053596d9SAndreas Noever "hotplug event from non existent port %llx:%x (unplug: %d)\n", 319053596d9SAndreas Noever ev->route, ev->port, ev->unplug); 3208f965efdSMika Westerberg goto put_sw; 321053596d9SAndreas Noever } 322053596d9SAndreas Noever port = &sw->ports[ev->port]; 323053596d9SAndreas Noever if (tb_is_upstream_port(port)) { 324dfe40ca4SMika Westerberg tb_dbg(tb, "hotplug event for upstream port %llx:%x (unplug: %d)\n", 325053596d9SAndreas Noever ev->route, ev->port, ev->unplug); 3268f965efdSMika Westerberg goto put_sw; 327053596d9SAndreas Noever } 328053596d9SAndreas Noever if (ev->unplug) { 329dfe40ca4SMika Westerberg if (tb_port_has_remote(port)) { 330053596d9SAndreas Noever tb_port_info(port, "unplugged\n"); 331aae20bb6SLukas Wunner tb_sw_set_unplugged(port->remote->sw); 3323364f0c1SAndreas Noever tb_free_invalid_tunnels(tb); 333bfe778acSMika Westerberg tb_switch_remove(port->remote->sw); 334053596d9SAndreas Noever port->remote = NULL; 335dfe40ca4SMika Westerberg if (port->dual_link_port) 336dfe40ca4SMika Westerberg port->dual_link_port->remote = NULL; 337053596d9SAndreas Noever } else { 338053596d9SAndreas Noever tb_port_info(port, 339053596d9SAndreas Noever "got unplug event for disconnected port, ignoring\n"); 340053596d9SAndreas Noever } 341053596d9SAndreas Noever } else if (port->remote) { 342053596d9SAndreas Noever tb_port_info(port, 343053596d9SAndreas Noever "got plug event for connected port, ignoring\n"); 344053596d9SAndreas Noever } else { 345053596d9SAndreas Noever tb_port_info(port, "hotplug: scanning\n"); 346053596d9SAndreas Noever tb_scan_port(port); 347053596d9SAndreas Noever if (!port->remote) { 348053596d9SAndreas Noever tb_port_info(port, "hotplug: no switch found\n"); 349053596d9SAndreas Noever } else if (port->remote->sw->config.depth > 1) { 350053596d9SAndreas Noever tb_sw_warn(port->remote->sw, 351053596d9SAndreas Noever "hotplug: chaining not supported\n"); 3523364f0c1SAndreas Noever } else { 3533364f0c1SAndreas Noever tb_sw_info(port->remote->sw, 3543364f0c1SAndreas Noever "hotplug: activating pcie devices\n"); 3553364f0c1SAndreas Noever tb_activate_pcie_devices(tb); 356053596d9SAndreas Noever } 357053596d9SAndreas Noever } 3588f965efdSMika Westerberg 3598f965efdSMika Westerberg put_sw: 3608f965efdSMika Westerberg tb_switch_put(sw); 361d6cc51cdSAndreas Noever out: 362d6cc51cdSAndreas Noever mutex_unlock(&tb->lock); 363d6cc51cdSAndreas Noever kfree(ev); 364d6cc51cdSAndreas Noever } 365d6cc51cdSAndreas Noever 366d6cc51cdSAndreas Noever /** 367d6cc51cdSAndreas Noever * tb_schedule_hotplug_handler() - callback function for the control channel 368d6cc51cdSAndreas Noever * 369d6cc51cdSAndreas Noever * Delegates to tb_handle_hotplug. 370d6cc51cdSAndreas Noever */ 37181a54b5eSMika Westerberg static void tb_handle_event(struct tb *tb, enum tb_cfg_pkg_type type, 37281a54b5eSMika Westerberg const void *buf, size_t size) 373d6cc51cdSAndreas Noever { 37481a54b5eSMika Westerberg const struct cfg_event_pkg *pkg = buf; 37581a54b5eSMika Westerberg struct tb_hotplug_event *ev; 37681a54b5eSMika Westerberg u64 route; 37781a54b5eSMika Westerberg 37881a54b5eSMika Westerberg if (type != TB_CFG_PKG_EVENT) { 37981a54b5eSMika Westerberg tb_warn(tb, "unexpected event %#x, ignoring\n", type); 38081a54b5eSMika Westerberg return; 38181a54b5eSMika Westerberg } 38281a54b5eSMika Westerberg 38381a54b5eSMika Westerberg route = tb_cfg_get_route(&pkg->header); 38481a54b5eSMika Westerberg 38581a54b5eSMika Westerberg if (tb_cfg_error(tb->ctl, route, pkg->port, 38681a54b5eSMika Westerberg TB_CFG_ERROR_ACK_PLUG_EVENT)) { 38781a54b5eSMika Westerberg tb_warn(tb, "could not ack plug event on %llx:%x\n", route, 38881a54b5eSMika Westerberg pkg->port); 38981a54b5eSMika Westerberg } 39081a54b5eSMika Westerberg 39181a54b5eSMika Westerberg ev = kmalloc(sizeof(*ev), GFP_KERNEL); 392d6cc51cdSAndreas Noever if (!ev) 393d6cc51cdSAndreas Noever return; 394d6cc51cdSAndreas Noever INIT_WORK(&ev->work, tb_handle_hotplug); 395d6cc51cdSAndreas Noever ev->tb = tb; 396d6cc51cdSAndreas Noever ev->route = route; 39781a54b5eSMika Westerberg ev->port = pkg->port; 39881a54b5eSMika Westerberg ev->unplug = pkg->unplug; 399d6cc51cdSAndreas Noever queue_work(tb->wq, &ev->work); 400d6cc51cdSAndreas Noever } 401d6cc51cdSAndreas Noever 4029d3cce0bSMika Westerberg static void tb_stop(struct tb *tb) 403d6cc51cdSAndreas Noever { 4049d3cce0bSMika Westerberg struct tb_cm *tcm = tb_priv(tb); 40593f36adeSMika Westerberg struct tb_tunnel *tunnel; 40693f36adeSMika Westerberg struct tb_tunnel *n; 4073364f0c1SAndreas Noever 4083364f0c1SAndreas Noever /* tunnels are only present after everything has been initialized */ 4099d3cce0bSMika Westerberg list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list) { 41093f36adeSMika Westerberg tb_tunnel_deactivate(tunnel); 41193f36adeSMika Westerberg tb_tunnel_free(tunnel); 4123364f0c1SAndreas Noever } 413bfe778acSMika Westerberg tb_switch_remove(tb->root_switch); 4149d3cce0bSMika Westerberg tcm->hotplug_active = false; /* signal tb_handle_hotplug to quit */ 415d6cc51cdSAndreas Noever } 416d6cc51cdSAndreas Noever 4179d3cce0bSMika Westerberg static int tb_start(struct tb *tb) 418d6cc51cdSAndreas Noever { 4199d3cce0bSMika Westerberg struct tb_cm *tcm = tb_priv(tb); 420bfe778acSMika Westerberg int ret; 421d6cc51cdSAndreas Noever 422bfe778acSMika Westerberg tb->root_switch = tb_switch_alloc(tb, &tb->dev, 0); 423a25c8b2fSAndreas Noever if (!tb->root_switch) 4249d3cce0bSMika Westerberg return -ENOMEM; 425a25c8b2fSAndreas Noever 426e6b245ccSMika Westerberg /* 427e6b245ccSMika Westerberg * ICM firmware upgrade needs running firmware and in native 428e6b245ccSMika Westerberg * mode that is not available so disable firmware upgrade of the 429e6b245ccSMika Westerberg * root switch. 430e6b245ccSMika Westerberg */ 431e6b245ccSMika Westerberg tb->root_switch->no_nvm_upgrade = true; 432e6b245ccSMika Westerberg 433bfe778acSMika Westerberg ret = tb_switch_configure(tb->root_switch); 434bfe778acSMika Westerberg if (ret) { 435bfe778acSMika Westerberg tb_switch_put(tb->root_switch); 436bfe778acSMika Westerberg return ret; 437bfe778acSMika Westerberg } 438bfe778acSMika Westerberg 439bfe778acSMika Westerberg /* Announce the switch to the world */ 440bfe778acSMika Westerberg ret = tb_switch_add(tb->root_switch); 441bfe778acSMika Westerberg if (ret) { 442bfe778acSMika Westerberg tb_switch_put(tb->root_switch); 443bfe778acSMika Westerberg return ret; 444bfe778acSMika Westerberg } 445bfe778acSMika Westerberg 4469da672a4SAndreas Noever /* Full scan to discover devices added before the driver was loaded. */ 4479da672a4SAndreas Noever tb_scan_switch(tb->root_switch); 4480414bec5SMika Westerberg /* Find out tunnels created by the boot firmware */ 4490414bec5SMika Westerberg tb_discover_tunnels(tb->root_switch); 4503364f0c1SAndreas Noever tb_activate_pcie_devices(tb); 4519da672a4SAndreas Noever 452d6cc51cdSAndreas Noever /* Allow tb_handle_hotplug to progress events */ 4539d3cce0bSMika Westerberg tcm->hotplug_active = true; 4549d3cce0bSMika Westerberg return 0; 455d6cc51cdSAndreas Noever } 456d6cc51cdSAndreas Noever 4579d3cce0bSMika Westerberg static int tb_suspend_noirq(struct tb *tb) 45823dd5bb4SAndreas Noever { 4599d3cce0bSMika Westerberg struct tb_cm *tcm = tb_priv(tb); 4609d3cce0bSMika Westerberg 461daa5140fSMika Westerberg tb_dbg(tb, "suspending...\n"); 46223dd5bb4SAndreas Noever tb_switch_suspend(tb->root_switch); 4639d3cce0bSMika Westerberg tcm->hotplug_active = false; /* signal tb_handle_hotplug to quit */ 464daa5140fSMika Westerberg tb_dbg(tb, "suspend finished\n"); 4659d3cce0bSMika Westerberg 4669d3cce0bSMika Westerberg return 0; 46723dd5bb4SAndreas Noever } 46823dd5bb4SAndreas Noever 4699d3cce0bSMika Westerberg static int tb_resume_noirq(struct tb *tb) 47023dd5bb4SAndreas Noever { 4719d3cce0bSMika Westerberg struct tb_cm *tcm = tb_priv(tb); 47293f36adeSMika Westerberg struct tb_tunnel *tunnel, *n; 4739d3cce0bSMika Westerberg 474daa5140fSMika Westerberg tb_dbg(tb, "resuming...\n"); 47523dd5bb4SAndreas Noever 47623dd5bb4SAndreas Noever /* remove any pci devices the firmware might have setup */ 47723dd5bb4SAndreas Noever tb_switch_reset(tb, 0); 47823dd5bb4SAndreas Noever 47923dd5bb4SAndreas Noever tb_switch_resume(tb->root_switch); 48023dd5bb4SAndreas Noever tb_free_invalid_tunnels(tb); 48123dd5bb4SAndreas Noever tb_free_unplugged_children(tb->root_switch); 4829d3cce0bSMika Westerberg list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list) 48393f36adeSMika Westerberg tb_tunnel_restart(tunnel); 4849d3cce0bSMika Westerberg if (!list_empty(&tcm->tunnel_list)) { 48523dd5bb4SAndreas Noever /* 48623dd5bb4SAndreas Noever * the pcie links need some time to get going. 48723dd5bb4SAndreas Noever * 100ms works for me... 48823dd5bb4SAndreas Noever */ 489daa5140fSMika Westerberg tb_dbg(tb, "tunnels restarted, sleeping for 100ms\n"); 49023dd5bb4SAndreas Noever msleep(100); 49123dd5bb4SAndreas Noever } 49223dd5bb4SAndreas Noever /* Allow tb_handle_hotplug to progress events */ 4939d3cce0bSMika Westerberg tcm->hotplug_active = true; 494daa5140fSMika Westerberg tb_dbg(tb, "resume finished\n"); 4959d3cce0bSMika Westerberg 4969d3cce0bSMika Westerberg return 0; 4979d3cce0bSMika Westerberg } 4989d3cce0bSMika Westerberg 4999d3cce0bSMika Westerberg static const struct tb_cm_ops tb_cm_ops = { 5009d3cce0bSMika Westerberg .start = tb_start, 5019d3cce0bSMika Westerberg .stop = tb_stop, 5029d3cce0bSMika Westerberg .suspend_noirq = tb_suspend_noirq, 5039d3cce0bSMika Westerberg .resume_noirq = tb_resume_noirq, 50481a54b5eSMika Westerberg .handle_event = tb_handle_event, 5059d3cce0bSMika Westerberg }; 5069d3cce0bSMika Westerberg 5079d3cce0bSMika Westerberg struct tb *tb_probe(struct tb_nhi *nhi) 5089d3cce0bSMika Westerberg { 5099d3cce0bSMika Westerberg struct tb_cm *tcm; 5109d3cce0bSMika Westerberg struct tb *tb; 5119d3cce0bSMika Westerberg 512630b3affSLukas Wunner if (!x86_apple_machine) 513f67cf491SMika Westerberg return NULL; 514f67cf491SMika Westerberg 5159d3cce0bSMika Westerberg tb = tb_domain_alloc(nhi, sizeof(*tcm)); 5169d3cce0bSMika Westerberg if (!tb) 5179d3cce0bSMika Westerberg return NULL; 5189d3cce0bSMika Westerberg 519f67cf491SMika Westerberg tb->security_level = TB_SECURITY_NONE; 5209d3cce0bSMika Westerberg tb->cm_ops = &tb_cm_ops; 5219d3cce0bSMika Westerberg 5229d3cce0bSMika Westerberg tcm = tb_priv(tb); 5239d3cce0bSMika Westerberg INIT_LIST_HEAD(&tcm->tunnel_list); 5249d3cce0bSMika Westerberg 5259d3cce0bSMika Westerberg return tb; 52623dd5bb4SAndreas Noever } 527