Received: by 2002:ac0:8c8e:0:0:0:0:0 with SMTP id r14csp589520ima; Wed, 6 Feb 2019 05:18:24 -0800 (PST) X-Google-Smtp-Source: AHgI3IYUgOoqgkJ8+V2FcAx8OLaRIGmvtGSrXgvELphbL5v0MUY+NQfleZF0ttHscmpoWSXg8u6e X-Received: by 2002:a63:c408:: with SMTP id h8mr248622pgd.86.1549459104558; Wed, 06 Feb 2019 05:18:24 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1549459104; cv=none; d=google.com; s=arc-20160816; b=HjWWwSfGxDjeYSkTaR6iOu8yGHMEkh6ei0cA3GQqb7vPb3kekJx+s1wZuQmv5GlR+g jPrnk6UU67uEOHlHnf+88IeeYFvQzbEEL4YC+ZhSQ8gWM3mVnoH25oar0uyvsjFYd2ff xYYyE+epGROK0g2wQ0IpvsTb1fmHEw72MKobW1FjdztiPQ9hbOfLxSmbEU/2/6/sEYCD 3qpo12F2jxkxC61zM/olK0GFoA7kEat+mJy9LHdwBgBafcwDk5kTX9x1ip7PxNHNPc29 SqbvrVE3pWMD7hzKmyll2lhwQjSO3q+S8APaNm1AiRprA1TzYDANSkiK2GHdcL03DC3B 2aYg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from; bh=PeaMHF/J6e+MTwp7/G97UB2a8zMXMCPw+LlLwLXfd2s=; b=UvaEbu4ANFPAVtGg4FDd4m8TdC7JSokjZ6rlxr22FRKSFeIRCnO/pi/v/rKnZ1A/+7 FLHyN5uTUS7EXJOPblKt/dsRCB3qGYRqtNM6R6X9bswAL1TX2bzbJr6s5dVHxB+MI9BB frw3nOFVIkA0V7ngO62Daw0ZIlWF9U+NjKIMtEh8v+8OdEH3jXH9rc/rfKGrOuoXSn/t 1/6QKhuqMKwac21k/Xwr4KHrFi/znd35V+CL8KEv1xhu3Hzxs6uSFqwPbP84690b2Ezb 9PINbS+mt4iRc3Q6sTYv3OukbKVuUiIMrz5aQx9/4/E8olEp9YIsZ7ZakKIYivEqcv9G ZWKg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id 128si6099939pfe.4.2019.02.06.05.18.08; Wed, 06 Feb 2019 05:18:24 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730693AbfBFNRw (ORCPT + 99 others); Wed, 6 Feb 2019 08:17:52 -0500 Received: from mga05.intel.com ([192.55.52.43]:56949 "EHLO mga05.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730585AbfBFNRv (ORCPT ); Wed, 6 Feb 2019 08:17:51 -0500 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga004.fm.intel.com ([10.253.24.48]) by fmsmga105.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 06 Feb 2019 05:17:47 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.58,340,1544515200"; d="scan'208";a="142036829" Received: from black.fi.intel.com ([10.237.72.28]) by fmsmga004.fm.intel.com with ESMTP; 06 Feb 2019 05:17:45 -0800 Received: by black.fi.intel.com (Postfix, from userid 1001) id 37A3F84E; Wed, 6 Feb 2019 15:17:39 +0200 (EET) From: Mika Westerberg To: linux-kernel@vger.kernel.org Cc: Michael Jamet , Yehezkel Bernat , Andreas Noever , Lukas Wunner , "David S . Miller" , Mika Westerberg , Andy Shevchenko , netdev@vger.kernel.org Subject: [PATCH v2 17/28] thunderbolt: Add support for full PCIe daisy chains Date: Wed, 6 Feb 2019 16:17:27 +0300 Message-Id: <20190206131738.43696-18-mika.westerberg@linux.intel.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190206131738.43696-1-mika.westerberg@linux.intel.com> References: <20190206131738.43696-1-mika.westerberg@linux.intel.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Currently the software connection manager (tb.c) has only supported creating a single PCIe tunnel, no PCIe device daisy chaining has been supported so far. This updates the software connection manager so that it now can create PCIe tunnels for full chain of six devices. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tb.c | 174 +++++++++++++++++++++++---------------- 1 file changed, 104 insertions(+), 70 deletions(-) diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 371633e17916..f2b23b290b63 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -1,8 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Thunderbolt Cactus Ridge driver - bus logic (NHI independent) + * Thunderbolt driver - bus logic (NHI independent) * * Copyright (c) 2014 Andreas Noever + * Copyright (C) 2019, Intel Corporation */ #include @@ -50,8 +51,15 @@ static void tb_discover_tunnels(struct tb_switch *sw) } /* Find and add existing tunnels */ - if (tunnel) + if (tunnel) { + struct tb_port *p; + + /* Firmware added switches are always authorized */ + tb_for_each_port(p, tunnel->src_port, tunnel->dst_port) + p->sw->boot = true; + list_add_tail(&tunnel->list, &tcm->tunnel_list); + } } for (i = 1; i <= sw->config.max_port_number; i++) { @@ -63,6 +71,16 @@ static void tb_discover_tunnels(struct tb_switch *sw) } } +static void tb_switch_authorize(struct work_struct *work) +{ + struct tb_switch *sw = container_of(work, typeof(*sw), work); + + mutex_lock(&sw->tb->lock); + if (!sw->is_unplugged) + tb_domain_approve_switch(sw->tb, sw); + mutex_unlock(&sw->tb->lock); +} + static void tb_scan_port(struct tb_port *port); /** @@ -80,6 +98,7 @@ static void tb_scan_switch(struct tb_switch *sw) */ static void tb_scan_port(struct tb_port *port) { + struct tb_cm *tcm = tb_priv(port->sw->tb); struct tb_switch *sw; if (tb_is_upstream_port(port)) return; @@ -106,6 +125,14 @@ static void tb_scan_port(struct tb_port *port) return; } + /* + * Do not send uevents until we have discovered all existing + * tunnels and know which switches were authorized already by + * the boot firmware. + */ + if (!tcm->hotplug_active) + dev_set_uevent_suppress(&sw->dev, true); + sw->authorized = true; if (tb_switch_add(sw)) { @@ -113,6 +140,9 @@ static void tb_scan_port(struct tb_port *port) return; } + INIT_WORK(&sw->work, tb_switch_authorize); + queue_work(sw->tb->wq, &sw->work); + port->remote = tb_upstream_port(sw); tb_upstream_port(sw)->remote = port; tb_scan_switch(sw); @@ -149,6 +179,7 @@ static void tb_free_unplugged_children(struct tb_switch *sw) if (!port->remote) continue; if (port->remote->sw->is_unplugged) { + cancel_work_sync(&port->remote->sw->work); tb_switch_remove(port->remote->sw); port->remote = NULL; } else { @@ -197,72 +228,58 @@ static struct tb_port *tb_find_unused_down_port(struct tb_switch *sw) return NULL; } -/** - * tb_activate_pcie_devices() - scan for and activate PCIe devices - * - * This method is somewhat ad hoc. For now it only supports one device - * per port and only devices at depth 1. - */ -static void tb_activate_pcie_devices(struct tb *tb) +static int tb_tunnel_pci(struct tb *tb, struct tb_switch *sw) { - int i; - int cap; - u32 data; - struct tb_switch *sw; - struct tb_port *up_port; - struct tb_port *down_port; - struct tb_tunnel *tunnel; struct tb_cm *tcm = tb_priv(tb); + struct tb_switch *parent_sw; + struct tb_port *up, *down; + struct tb_tunnel *tunnel; - /* scan for pcie devices at depth 1*/ - for (i = 1; i <= tb->root_switch->config.max_port_number; i++) { - if (tb_is_upstream_port(&tb->root_switch->ports[i])) - continue; - if (tb->root_switch->ports[i].config.type != TB_TYPE_PORT) - continue; - if (!tb->root_switch->ports[i].remote) - continue; - sw = tb->root_switch->ports[i].remote->sw; - up_port = tb_find_pci_up_port(sw); - if (!up_port) { - tb_sw_info(sw, "no PCIe devices found, aborting\n"); - continue; - } + up = tb_find_pci_up_port(sw); + if (!up) + return 0; - /* check whether port is already activated */ - cap = up_port->cap_adap; - if (!cap) - continue; - if (tb_port_read(up_port, &data, TB_CFG_PORT, cap, 1)) - continue; - if (data & 0x80000000) { - tb_port_info(up_port, - "PCIe port already activated, aborting\n"); - continue; - } + /* + * Look up available down port. Since we are chaining, it is + * typically found right above this switch. + */ + down = NULL; + parent_sw = tb_to_switch(sw->dev.parent); + while (parent_sw) { + down = tb_find_unused_down_port(parent_sw); + if (down) + break; + parent_sw = tb_to_switch(parent_sw->dev.parent); + } - down_port = tb_find_unused_down_port(tb->root_switch); - if (!down_port) { - tb_port_info(up_port, - "All PCIe down ports are occupied, aborting\n"); - continue; - } - tunnel = tb_tunnel_alloc_pci(tb, up_port, down_port); - if (!tunnel) { - tb_port_info(up_port, - "PCIe tunnel allocation failed, aborting\n"); - continue; - } + if (!down) + return 0; - if (tb_tunnel_activate(tunnel)) { - tb_port_info(up_port, - "PCIe tunnel activation failed, aborting\n"); - tb_tunnel_free(tunnel); - continue; - } + tunnel = tb_tunnel_alloc_pci(tb, up, down); + if (!tunnel) + return -EIO; - list_add(&tunnel->list, &tcm->tunnel_list); + if (tb_tunnel_activate(tunnel)) { + tb_port_info(up, + "PCIe tunnel activation failed, aborting\n"); + tb_tunnel_free(tunnel); + return -EIO; } + list_add_tail(&tunnel->list, &tcm->tunnel_list); + + return 0; +} + +static int tb_approve_switch(struct tb *tb, struct tb_switch *sw) +{ + /* + * Already authorized by the boot firmware so no need to do + * anything here. + */ + if (sw->boot) + return 0; + + return tb_tunnel_pci(tb, sw); } /* hotplug handling */ @@ -316,6 +333,7 @@ static void tb_handle_hotplug(struct work_struct *work) tb_port_info(port, "unplugged\n"); tb_sw_set_unplugged(port->remote->sw); tb_free_invalid_tunnels(tb); + cancel_work_sync(&sw->work); tb_switch_remove(port->remote->sw); port->remote = NULL; } else { @@ -328,16 +346,8 @@ static void tb_handle_hotplug(struct work_struct *work) } else { tb_port_info(port, "hotplug: scanning\n"); tb_scan_port(port); - if (!port->remote) { + if (!port->remote) tb_port_info(port, "hotplug: no switch found\n"); - } else if (port->remote->sw->config.depth > 1) { - tb_sw_warn(port->remote->sw, - "hotplug: chaining not supported\n"); - } else { - tb_sw_info(port->remote->sw, - "hotplug: activating pcie devices\n"); - tb_activate_pcie_devices(tb); - } } out: mutex_unlock(&tb->lock); @@ -395,6 +405,27 @@ static void tb_stop(struct tb *tb) tcm->hotplug_active = false; /* signal tb_handle_hotplug to quit */ } +static int tb_scan_finalize_switch(struct device *dev, void *data) +{ + if (tb_is_switch(dev)) { + struct tb_switch *sw = tb_to_switch(dev); + + /* + * If we found that the switch was already setup by the + * boot firmware, mark it as authorized now before we + * send uevent to userspace. + */ + if (sw->boot) + sw->authorized = 1; + + dev_set_uevent_suppress(dev, false); + kobject_uevent(&dev->kobj, KOBJ_ADD); + device_for_each_child(dev, NULL, tb_scan_finalize_switch); + } + + return 0; +} + static int tb_start(struct tb *tb) { struct tb_cm *tcm = tb_priv(tb); @@ -428,7 +459,9 @@ static int tb_start(struct tb *tb) tb_scan_switch(tb->root_switch); /* Find out tunnels created by the boot firmware */ tb_discover_tunnels(tb->root_switch); - tb_activate_pcie_devices(tb); + /* Make the discovered switches available to the userspace */ + device_for_each_child(&tb->root_switch->dev, NULL, + tb_scan_finalize_switch); /* Allow tb_handle_hotplug to progress events */ tcm->hotplug_active = true; @@ -483,6 +516,7 @@ static const struct tb_cm_ops tb_cm_ops = { .suspend_noirq = tb_suspend_noirq, .resume_noirq = tb_resume_noirq, .handle_event = tb_handle_event, + .approve_switch = tb_approve_switch, }; struct tb *tb_probe(struct tb_nhi *nhi) -- 2.20.1