/** * tb_pci_port_active() - activate/deactivate PCI capability * * Return: Returns 0 on success or an error code on failure. */ static int tb_pci_port_active(struct tb_port *port, bool active) { u32 word = active ? 0x80000000 : 0x0; int cap = tb_port_find_cap(port, TB_PORT_CAP_ADAP); if (cap < 0) { tb_port_warn(port, "TB_PORT_CAP_ADAP not found: %d\n", cap); return cap; } return tb_port_write(port, &word, TB_CFG_PORT, cap, 1); }
/** * tb_wait_for_port() - wait for a port to become ready * * Wait up to 1 second for a port to reach state TB_PORT_UP. If * wait_if_unplugged is set then we also wait if the port is in state * TB_PORT_UNPLUGGED (it takes a while for the device to be registered after * switch resume). Otherwise we only wait if a device is registered but the link * has not yet been established. * * Return: Returns an error code on failure. Returns 0 if the port is not * connected or failed to reach state TB_PORT_UP within one second. Returns 1 * if the port is connected and in state TB_PORT_UP. */ int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged) { int retries = 10; int state; if (!port->cap_phy) { tb_port_WARN(port, "does not have PHY\n"); return -EINVAL; } if (tb_is_upstream_port(port)) { tb_port_WARN(port, "is the upstream port\n"); return -EINVAL; } while (retries--) { state = tb_port_state(port); if (state < 0) return state; if (state == TB_PORT_DISABLED) { tb_port_info(port, "is disabled (state: 0)\n"); return 0; } if (state == TB_PORT_UNPLUGGED) { if (wait_if_unplugged) { /* used during resume */ tb_port_info(port, "is unplugged (state: 7), retrying...\n"); msleep(100); continue; } tb_port_info(port, "is unplugged (state: 7)\n"); return 0; } if (state == TB_PORT_UP) { tb_port_info(port, "is connected, link is up (state: 2)\n"); return 1; } /* * After plug-in the state is TB_PORT_CONNECTING. Give it some * time. */ tb_port_info(port, "is connected, link is not up (state: %d), retrying...\n", state); msleep(100); } tb_port_warn(port, "failed to reach state TB_PORT_UP. Ignoring port...\n"); return 0; }
int tb_switch_resume(struct tb_switch *sw) { int i, err; u64 uid; tb_sw_info(sw, "resuming switch\n"); err = tb_drom_read_uid_only(sw, &uid); if (err) { tb_sw_warn(sw, "uid read failed\n"); return err; } if (sw != sw->tb->root_switch && sw->uid != uid) { tb_sw_info(sw, "changed while suspended (uid %#llx -> %#llx)\n", sw->uid, uid); return -ENODEV; } /* upload configuration */ err = tb_sw_write(sw, 1 + (u32 *) &sw->config, TB_CFG_SWITCH, 1, 3); if (err) return err; err = tb_plug_events_active(sw, true); if (err) return err; /* check for surviving downstream switches */ 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 (tb_wait_for_port(port, true) <= 0 || tb_switch_resume(port->remote->sw)) { tb_port_warn(port, "lost during suspend, disconnecting\n"); tb_sw_set_unplugged(port->remote->sw); } } return 0; }