Message ID | 0ab165a4a35c0b60f29d4c306c653ead14fcd8f9.1489145162.git.lukas@wunner.de (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Fri, Mar 10, 2017 at 2:23 PM, Lukas Wunner <lukas@wunner.de> wrote: > +/** > + * pci_is_thunderbolt_attached - whether device is on a Thunderbolt daisy chain > + * @pdev: PCI device to check > + * > + * Walk upwards from @pdev and check for each encountered bridge if it's part > + * of a Thunderbolt controller. Reaching the host bridge means @pdev is not > + * Thunderbolt-attached. (But rather soldered to the mainboard usually.) The "soldered to the mainboard" comment is misleading. We'll reach the host bridge and return "false" for any non-Thunderbolt-attached device, including all plug-in PCI and PCIe devices. > + */ > +static inline bool pci_is_thunderbolt_attached(struct pci_dev *pdev) > +{ > + struct pci_dev *parent = pdev; > + > + if (pdev->is_thunderbolt) > + return true; > + > + while ((parent = pci_upstream_bridge(parent))) > + if (parent->is_thunderbolt) > + return true; > + > + return false; > +} > + > /* provide the legacy pci_dma_* API */ > #include <linux/pci-dma-compat.h> > > -- > 2.11.0 >
On Fri, Mar 10, 2017 at 02:47:04PM -0600, Bjorn Helgaas wrote: > On Fri, Mar 10, 2017 at 2:23 PM, Lukas Wunner <lukas@wunner.de> wrote: > > +/** > > + * pci_is_thunderbolt_attached - whether device is on a Thunderbolt daisy chain > > + * @pdev: PCI device to check > > + * > > + * Walk upwards from @pdev and check for each encountered bridge if it's part > > + * of a Thunderbolt controller. Reaching the host bridge means @pdev is not > > + * Thunderbolt-attached. (But rather soldered to the mainboard usually.) > > The "soldered to the mainboard" comment is misleading. We'll reach > the host bridge and return "false" for any non-Thunderbolt-attached > device, including all plug-in PCI and PCIe devices. It does say "usually". :-) Seriously though, for someone coming from one of the callers of pci_is_thunderbolt_attached() and trying to understand its meaning, it may not be helpful if I had left it at "Reaching the host bridge means @pdev is not Thunderbolt-attached." What is the *consequence* of that? Most Thunderbolt-equipped products have no other PCI expansion options, so indeed if the device is not on a Thunderbolt daisy chain it must be soldered to the mainboard. If one wants to be pedantic, one could add that it may alternatively be soldered to a *daughter*board. (Which is the case on the MacPro6,1, the black trashcan.) I was just trying to strike a balance between technical correctness, didactic quality and brevity. Best regards, Lukas > > > + */ > > +static inline bool pci_is_thunderbolt_attached(struct pci_dev *pdev) > > +{ > > + struct pci_dev *parent = pdev; > > + > > + if (pdev->is_thunderbolt) > > + return true; > > + > > + while ((parent = pci_upstream_bridge(parent))) > > + if (parent->is_thunderbolt) > > + return true; > > + > > + return false; > > +} > > + > > /* provide the legacy pci_dma_* API */ > > #include <linux/pci-dma-compat.h> > > > > -- > > 2.11.0
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index cb17db242f30..45c2b8144911 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -3,6 +3,8 @@ #define PCI_FIND_CAP_TTL 48 +#define PCI_VSEC_ID_INTEL_TBT 0x1234 /* Thunderbolt */ + extern const unsigned char pcie_link_speed[]; bool pcie_cap_has_lnkctl(const struct pci_dev *dev); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 204960e70333..7963ecc6d85f 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1208,6 +1208,24 @@ void set_pcie_hotplug_bridge(struct pci_dev *pdev) pdev->is_hotplug_bridge = 1; } +static void set_pcie_thunderbolt(struct pci_dev *dev) +{ + int vsec = 0; + u32 header; + + while ((vsec = pci_find_next_ext_capability(dev, vsec, + PCI_EXT_CAP_ID_VNDR))) { + pci_read_config_dword(dev, vsec + PCI_VNDR_HEADER, &header); + + /* Is the device part of a Thunderbolt controller? */ + if (dev->vendor == PCI_VENDOR_ID_INTEL && + PCI_VNDR_HEADER_ID(header) == PCI_VSEC_ID_INTEL_TBT) { + dev->is_thunderbolt = 1; + return; + } + } +} + /** * pci_ext_cfg_is_aliased - is ext config space just an alias of std config? * @dev: PCI device @@ -1360,6 +1378,9 @@ int pci_setup_device(struct pci_dev *dev) /* need to have dev->class ready */ dev->cfg_size = pci_cfg_space_size(dev); + /* need to have dev->cfg_size ready */ + set_pcie_thunderbolt(dev); + /* "Unknown power state" */ dev->current_state = PCI_UNKNOWN; diff --git a/include/linux/pci.h b/include/linux/pci.h index e2d1a124216a..a37ecd52b35b 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -358,6 +358,7 @@ struct pci_dev { unsigned int is_virtfn:1; unsigned int reset_fn:1; unsigned int is_hotplug_bridge:1; + unsigned int is_thunderbolt:1; /* Thunderbolt controller */ unsigned int __aer_firmware_first_valid:1; unsigned int __aer_firmware_first:1; unsigned int broken_intx_masking:1; @@ -2171,6 +2172,28 @@ static inline bool pci_ari_enabled(struct pci_bus *bus) return bus->self && bus->self->ari_enabled; } +/** + * pci_is_thunderbolt_attached - whether device is on a Thunderbolt daisy chain + * @pdev: PCI device to check + * + * Walk upwards from @pdev and check for each encountered bridge if it's part + * of a Thunderbolt controller. Reaching the host bridge means @pdev is not + * Thunderbolt-attached. (But rather soldered to the mainboard usually.) + */ +static inline bool pci_is_thunderbolt_attached(struct pci_dev *pdev) +{ + struct pci_dev *parent = pdev; + + if (pdev->is_thunderbolt) + return true; + + while ((parent = pci_upstream_bridge(parent))) + if (parent->is_thunderbolt) + return true; + + return false; +} + /* provide the legacy pci_dma_* API */ #include <linux/pci-dma-compat.h>