lkml.org 
[lkml]   [2018]   [Sep]   [9]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Date
    Subject[PATCH 2/5] thunderbolt: Obtain PCI slot number from DROM
    The port numbers of PCIe adapters on a CIO switch and the slot numbers
    of their corresponding PCI devices don't necessarily match up in
    ascending order.

    E.g. Light Ridge in host mode (MacBookPro9,1) mixes them up like this:
    switch port 7 == pci slot 4 (PCIe downstream port)
    switch port 8 == pci slot 5 (PCIe downstream port)
    switch port 9 == pci slot 6 (PCIe downstream port)
    switch port 10 == pci slot 3 (PCIe downstream port)

    Light Ridge in endpoint mode (Sonnet Echo Express):
    switch port 7 == pci slot 4 (PCIe downstream port)
    switch port 8 == pci slot 5 (PCIe downstream port)
    switch port 9 (PCIe downstream port, disabled in DROM)
    switch port 10 == pci slot 0 (PCIe upstream port, pci slot 3 in DROM)

    Obtain the slot number used by a switch port from the DROM and store it
    in struct tb_port. This will allow us to correlate PCI devices with
    Thunderbolt ports. For space efficiency, use a union in struct tb_port
    to store attributes that are specific to certain port types.
    Attributes specific to TB_TYPE_PORT could be moved into a separate
    struct within the union.

    The slot number occupies 3 bits in the third byte of PCI DROM entries.
    The purpose of the remaining 5 bits is unknown (function number?).

    Downstream port entries have 3 bytes, upstream port entries 11 bytes.
    The slot number in upstream port entries is not that of the port itself
    (which is always zero), but that of the PCI endpoint device built into
    the Thunderbolt product. In the case of the Sonnet Echo Express, the
    upstream port specifies slot 3, which is used by a PLX switch.

    The purpose of the additional 8 bytes present on upstream ports is
    unknown (number of PCIe lanes, priority requirements, direct tunnel to
    the host desired?). Examples:

    01 00 64 00 00 00 00 00 (Sonnet Echo Express, PLX switch)
    01 40 0a 00 00 00 00 00 (Apple Gigabit Ethernet Adapter, BCM957762)

    Signed-off-by: Lukas Wunner <lukas@wunner.de>
    ---
    drivers/thunderbolt/eeprom.c | 21 +++++++++++++++++++++
    drivers/thunderbolt/switch.c | 14 +++++++++++++-
    drivers/thunderbolt/tb.h | 9 +++++++++
    3 files changed, 43 insertions(+), 1 deletion(-)

    diff --git a/drivers/thunderbolt/eeprom.c b/drivers/thunderbolt/eeprom.c
    index 3e8caf22c294..ce6e037fdb39 100644
    --- a/drivers/thunderbolt/eeprom.c
    +++ b/drivers/thunderbolt/eeprom.c
    @@ -236,6 +236,15 @@ struct tb_drom_entry_port {
    u8 unknown4:2;
    } __packed;

    +struct tb_drom_entry_pci {
    + /* BYTES 0-1 */
    + struct tb_drom_entry_header header;
    + /* BYTE 2 */
    + u8 unknown:5;
    + u8 slot:3;
    + /* BYTES 3-10 are only present on PCIe upstream ports */
    +} __packed;
    +

    /**
    * tb_eeprom_get_drom_offset - get drom offset within eeprom
    @@ -365,6 +374,18 @@ static int tb_drom_parse_entry_port(struct tb_switch *sw,
    if (entry->has_dual_link_port)
    port->dual_link_port =
    &port->sw->ports[entry->dual_link_port_nr];
    + } else if (type == TB_TYPE_PCIE_UP || type == TB_TYPE_PCIE_DOWN) {
    + struct tb_drom_entry_pci *entry = (void *) header;
    + if (header->len < sizeof(*entry)) {
    + tb_sw_warn(sw,
    + "port entry has size %#x (expected %#zx+)\n",
    + header->len, sizeof(struct tb_drom_entry_pci));
    + return -EIO;
    + }
    + port->pci.devfn = PCI_DEVFN(entry->slot, 0);
    + memcpy(port->pci.unknown, header + 1,
    + min(header->len - sizeof(struct tb_drom_entry_header),
    + sizeof(port->pci.unknown)));
    }
    return 0;
    }
    diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
    index 7442bc4c6433..54fd42250935 100644
    --- a/drivers/thunderbolt/switch.c
    +++ b/drivers/thunderbolt/switch.c
    @@ -577,6 +577,7 @@ int tb_port_clear_counter(struct tb_port *port, int counter)
    */
    static int tb_init_port(struct tb_port *port)
    {
    + struct tb *tb = port->sw->tb;
    int res;
    int cap;

    @@ -594,9 +595,20 @@ static int tb_init_port(struct tb_port *port)
    tb_port_WARN(port, "non switch port without a PHY\n");
    }

    - tb_dump_port(port->sw->tb, &port->config);
    + tb_dump_port(tb, &port->config);

    /* TODO: Read dual link port, DP port and more from EEPROM. */
    + switch (port->config.type) {
    + case TB_TYPE_PCIE_UP:
    + case TB_TYPE_PCIE_DOWN:
    + tb_info(tb, " PCI slot: %02x.0\n", PCI_SLOT(port->pci.devfn));
    + tb_info(tb, " PCI unknown data: %*ph\n",
    + (int)sizeof(port->pci.unknown), port->pci.unknown);
    + break;
    + default:
    + break;
    + }
    +
    return 0;

    }
    diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
    index 5067d69d0501..755183f0b257 100644
    --- a/drivers/thunderbolt/tb.h
    +++ b/drivers/thunderbolt/tb.h
    @@ -125,6 +125,9 @@ struct tb_switch {
    * @dual_link_port: If the switch is connected using two ports, points
    * to the other port.
    * @link_nr: Is this primary or secondary port on the dual_link.
    + * @pci: Data specific to PCIe adapters
    + * @pci.devfn: PCI slot/function according to DROM
    + * @pci.unknown: Unknown data in DROM (to be reverse-engineered)
    */
    struct tb_port {
    struct tb_regs_port_header config;
    @@ -136,6 +139,12 @@ struct tb_port {
    bool disabled;
    struct tb_port *dual_link_port;
    u8 link_nr:1;
    + union {
    + struct {
    + u8 devfn;
    + u8 unknown[9];
    + } pci;
    + };
    };

    /**
    --
    2.18.0
    \
     
     \ /
      Last update: 2018-09-09 23:44    [W:2.354 / U:0.016 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site