aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/thunderbolt/tb_pcib.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/thunderbolt/tb_pcib.c')
-rw-r--r--sys/dev/thunderbolt/tb_pcib.c614
1 files changed, 614 insertions, 0 deletions
diff --git a/sys/dev/thunderbolt/tb_pcib.c b/sys/dev/thunderbolt/tb_pcib.c
new file mode 100644
index 000000000000..00738984ad1c
--- /dev/null
+++ b/sys/dev/thunderbolt/tb_pcib.c
@@ -0,0 +1,614 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2022 Scott Long
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "opt_acpi.h"
+#include "opt_thunderbolt.h"
+
+/* PCIe bridge for Thunderbolt */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/malloc.h>
+#include <sys/sysctl.h>
+#include <sys/lock.h>
+#include <sys/param.h>
+#include <sys/endian.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/stdarg.h>
+#include <sys/rman.h>
+
+#include <machine/pci_cfgreg.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcib_private.h>
+#include <dev/pci/pci_private.h>
+
+#include <contrib/dev/acpica/include/acpi.h>
+#include <contrib/dev/acpica/include/accommon.h>
+#include <dev/acpica/acpivar.h>
+#include <dev/acpica/acpi_pcibvar.h>
+#include <machine/md_var.h>
+
+#include <dev/thunderbolt/tb_reg.h>
+#include <dev/thunderbolt/tb_pcib.h>
+#include <dev/thunderbolt/nhi_var.h>
+#include <dev/thunderbolt/nhi_reg.h>
+#include <dev/thunderbolt/tbcfg_reg.h>
+#include <dev/thunderbolt/tb_debug.h>
+#include "tb_if.h"
+
+static int tb_pcib_probe(device_t);
+static int tb_pcib_attach(device_t);
+static int tb_pcib_detach(device_t);
+static int tb_pcib_lc_mailbox(device_t, struct tb_lcmbox_cmd *);
+static int tb_pcib_pcie2cio_read(device_t, u_int, u_int, u_int,
+ uint32_t *);
+static int tb_pcib_pcie2cio_write(device_t, u_int, u_int, u_int, uint32_t);
+static int tb_pcib_find_ufp(device_t, device_t *);
+static int tb_pcib_get_debug(device_t, u_int *);
+
+static int tb_pci_probe(device_t);
+static int tb_pci_attach(device_t);
+static int tb_pci_detach(device_t);
+
+struct tb_pcib_ident {
+ uint16_t vendor;
+ uint16_t device;
+ uint16_t subvendor;
+ uint16_t subdevice;
+ uint32_t flags; /* This follows the tb_softc flags */
+ const char *desc;
+} tb_pcib_identifiers[] = {
+ { VENDOR_INTEL, TB_DEV_AR_2C, 0xffff, 0xffff, TB_GEN_TB3|TB_HWIF_AR,
+ "Thunderbolt 3 PCI-PCI Bridge (Alpine Ridge 2C)" },
+ { VENDOR_INTEL, TB_DEV_AR_LP, 0xffff, 0xffff, TB_GEN_TB3|TB_HWIF_AR,
+ "Thunderbolt 3 PCI-PCI Bridge (Alpine Ridge LP)" },
+ { VENDOR_INTEL, TB_DEV_AR_C_4C, 0xffff, 0xffff, TB_GEN_TB3|TB_HWIF_AR,
+ "Thunderbolt 3 PCI-PCI Bridge (Alpine Ridge C 4C)" },
+ { VENDOR_INTEL, TB_DEV_AR_C_2C, 0xffff, 0xffff, TB_GEN_TB3|TB_HWIF_AR,
+ "Thunderbolt 3 PCI-PCI Bridge C (Alpine Ridge C 2C)" },
+ { VENDOR_INTEL, TB_DEV_ICL_0, 0xffff, 0xffff, TB_GEN_TB3|TB_HWIF_ICL,
+ "Thunderbolt 3 PCI-PCI Bridge (IceLake)" },
+ { VENDOR_INTEL, TB_DEV_ICL_1, 0xffff, 0xffff, TB_GEN_TB3|TB_HWIF_ICL,
+ "Thunderbolt 3 PCI-PCI Bridge (IceLake)" },
+ { 0, 0, 0, 0, 0, NULL }
+};
+
+static struct tb_pcib_ident *
+tb_pcib_find_ident(device_t dev)
+{
+ struct tb_pcib_ident *n;
+ uint16_t v, d, sv, sd;
+
+ v = pci_get_vendor(dev);
+ d = pci_get_device(dev);
+ sv = pci_get_subvendor(dev);
+ sd = pci_get_subdevice(dev);
+
+ for (n = tb_pcib_identifiers; n->vendor != 0; n++) {
+ if ((n->vendor != v) || (n->device != d))
+ continue;
+ if (((n->subvendor != 0xffff) && (n->subvendor != sv)) ||
+ ((n->subdevice != 0xffff) && (n->subdevice != sd)))
+ continue;
+ return (n);
+ }
+
+ return (NULL);
+}
+
+static void
+tb_pcib_get_tunables(struct tb_pcib_softc *sc)
+{
+ char tmpstr[80], oid[80];
+
+ /* Set the default */
+ sc->debug = 0;
+
+ /* Grab global variables */
+ bzero(oid, 80);
+ if (TUNABLE_STR_FETCH("hw.tbolt.debug_level", oid, 80) != 0)
+ tb_parse_debug(&sc->debug, oid);
+
+ /* Grab instance variables */
+ bzero(oid, 80);
+ snprintf(tmpstr, sizeof(tmpstr), "dev.tbolt.%d.debug_level",
+ device_get_unit(sc->dev));
+ if (TUNABLE_STR_FETCH(tmpstr, oid, 80) != 0)
+ tb_parse_debug(&sc->debug, oid);
+
+ return;
+}
+
+static int
+tb_pcib_setup_sysctl(struct tb_pcib_softc *sc)
+{
+ struct sysctl_ctx_list *ctx = NULL;
+ struct sysctl_oid *tree = NULL;
+
+ ctx = device_get_sysctl_ctx(sc->dev);
+ if (ctx != NULL)
+ tree = device_get_sysctl_tree(sc->dev);
+
+ if (tree == NULL) {
+ tb_printf(sc, "Error: cannot create sysctl nodes\n");
+ return (EINVAL);
+ }
+ sc->sysctl_tree = tree;
+ sc->sysctl_ctx = ctx;
+
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree),
+ OID_AUTO, "debug_level", CTLTYPE_STRING|CTLFLAG_RW|CTLFLAG_MPSAFE,
+ &sc->debug, 0, tb_debug_sysctl, "A", "Thunderbolt debug level");
+
+ return (0);
+}
+
+/*
+ * This is used for both the PCI and ACPI attachments. It shouldn't return
+ * 0, doing so will force the ACPI attachment to fail.
+ */
+int
+tb_pcib_probe_common(device_t dev, char *desc)
+{
+ device_t ufp;
+ struct tb_pcib_ident *n;
+ char *suffix;
+
+ if ((n = tb_pcib_find_ident(dev)) != NULL) {
+ ufp = NULL;
+ if ((TB_FIND_UFP(dev, &ufp) == 0) && (ufp == dev))
+ suffix = "(Upstream port)";
+ else
+ suffix = "(Downstream port)";
+ snprintf(desc, TB_DESC_MAX, "%s %s", n->desc, suffix);
+ return (BUS_PROBE_VENDOR);
+ }
+ return (ENXIO);
+}
+
+static int
+tb_pcib_probe(device_t dev)
+{
+ char desc[TB_DESC_MAX];
+ int val;
+
+ if ((val = tb_pcib_probe_common(dev, desc)) <= 0)
+ device_set_desc_copy(dev, desc);
+
+ return (val);
+}
+
+int
+tb_pcib_attach_common(device_t dev)
+{
+ device_t ufp;
+ struct tb_pcib_ident *n;
+ struct tb_pcib_softc *sc;
+ uint32_t val;
+ int error;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->vsec = -1;
+
+ n = tb_pcib_find_ident(dev);
+ KASSERT(n != NULL, ("Cannot find TB ident"));
+ sc->flags = n->flags;
+
+ tb_pcib_get_tunables(sc);
+ tb_pcib_setup_sysctl(sc);
+
+ /* XXX Is this necessary for ACPI attachments? */
+ tb_debug(sc, DBG_BRIDGE, "busmaster status was %s\n",
+ (pci_read_config(dev, PCIR_COMMAND, 2) & PCIM_CMD_BUSMASTEREN)
+ ? "enabled" : "disabled");
+ pci_enable_busmaster(dev);
+
+ /*
+ * Determine if this is an upstream or downstream facing device, and
+ * whether it's the root of the Thunderbolt topology. It's too bad
+ * that there aren't unique PCI ID's to help with this.
+ */
+ ufp = NULL;
+ if ((TB_FIND_UFP(dev, &ufp) == 0) && (ufp != NULL)) {
+ if (ufp == dev) {
+ sc->flags |= TB_FLAGS_ISUFP;
+ if (TB_FIND_UFP(device_get_parent(dev), NULL) ==
+ EOPNOTSUPP) {
+ sc->flags |= TB_FLAGS_ISROOT;
+ }
+ }
+ }
+
+ /*
+ * Find the PCI Vendor Specific Extended Capability. It's the magic
+ * wand to configuring the Thunderbolt root bridges.
+ */
+ if (TB_IS_AR(sc) || TB_IS_TR(sc)) {
+ error = pci_find_extcap(dev, PCIZ_VENDOR, &sc->vsec);
+ if (error) {
+ tb_printf(sc, "Cannot find VSEC capability: %d\n",
+ error);
+ return (ENXIO);
+ }
+ }
+
+ /*
+ * Take the AR bridge out of low-power mode.
+ * XXX AR only?
+ */
+ if ((1 || TB_IS_AR(sc)) && TB_IS_ROOT(sc)) {
+ struct tb_lcmbox_cmd cmd;
+
+ cmd.cmd = LC_MBOXOUT_CMD_SXEXIT_TBT;
+ cmd.data_in = 0;
+
+ error = TB_LC_MAILBOX(dev, &cmd);
+ tb_debug(sc, DBG_BRIDGE, "SXEXIT returned error= %d resp= 0x%x "
+ "data= 0x%x\n", error, cmd.cmd_resp, cmd.data_out);
+ }
+
+ /* The downstream facing port on AR needs some help */
+ if (TB_IS_AR(sc) && TB_IS_DFP(sc)) {
+ tb_debug(sc, DBG_BRIDGE, "Doing AR L1 fixup\n");
+ val = pci_read_config(dev, sc->vsec + AR_VSCAP_1C, 4);
+ tb_debug(sc, DBG_BRIDGE|DBG_FULL, "VSEC+0x1c= 0x%08x\n", val);
+ val |= (1 << 8);
+ pci_write_config(dev, sc->vsec + AR_VSCAP_1C, val, 4);
+
+ val = pci_read_config(dev, sc->vsec + AR_VSCAP_B0, 4);
+ tb_debug(sc, DBG_BRIDGE|DBG_FULL, "VSEC+0xb0= 0x%08x\n", val);
+ val |= (1 << 12);
+ pci_write_config(dev, sc->vsec + AR_VSCAP_B0, val, 4);
+ }
+
+ return (0);
+}
+
+static int
+tb_pcib_attach(device_t dev)
+{
+ int error;
+
+ error = tb_pcib_attach_common(dev);
+ if (error)
+ return (error);
+ return (pcib_attach(dev));
+}
+
+static int
+tb_pcib_detach(device_t dev)
+{
+ struct tb_pcib_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ tb_debug(sc, DBG_BRIDGE|DBG_ROUTER|DBG_EXTRA, "tb_pcib_detach\n");
+
+ /* Put the AR bridge back to sleep */
+ /* XXX disable this until power control for downstream switches works */
+ if (0 && TB_IS_ROOT(sc)) {
+ struct tb_lcmbox_cmd cmd;
+
+ cmd.cmd = LC_MBOXOUT_CMD_GO2SX;
+ cmd.data_in = 0;
+
+ error = TB_LC_MAILBOX(dev, &cmd);
+ tb_debug(sc, DBG_BRIDGE, "SXEXIT returned error= %d resp= 0x%x "
+ "data= 0x%x\n", error, cmd.cmd_resp, cmd.data_out);
+ }
+
+ return (pcib_detach(dev));
+}
+
+/* Read/write the Link Controller registers in CFG space */
+static int
+tb_pcib_lc_mailbox(device_t dev, struct tb_lcmbox_cmd *cmd)
+{
+ struct tb_pcib_softc *sc;
+ uint32_t regcmd, result;
+ uint16_t m_in, m_out;
+ int vsec, i;
+
+ sc = device_get_softc(dev);
+ vsec = TB_PCIB_VSEC(dev);
+ if (vsec == -1)
+ return (EOPNOTSUPP);
+
+ if (TB_IS_AR(sc)) {
+ m_in = AR_LC_MBOX_IN;
+ m_out = AR_LC_MBOX_OUT;
+ } else if (TB_IS_ICL(sc)) {
+ m_in = ICL_LC_MBOX_IN;
+ m_out = ICL_LC_MBOX_OUT;
+ } else
+ return (EOPNOTSUPP);
+
+ /* Set the valid bit to signal we're sending a command */
+ regcmd = LC_MBOXOUT_VALID | (cmd->cmd & LC_MBOXOUT_CMD_MASK);
+ regcmd |= (cmd->data_in << LC_MBOXOUT_DATA_SHIFT);
+ tb_debug(sc, DBG_BRIDGE|DBG_FULL, "Writing LC cmd 0x%x\n", regcmd);
+ pci_write_config(dev, vsec + m_out, regcmd, 4);
+
+ for (i = 0; i < 10; i++) {
+ pause("nhi", 1 * hz);
+ result = pci_read_config(dev, vsec + m_in, 4);
+ tb_debug(sc, DBG_BRIDGE|DBG_FULL, "LC Mailbox= 0x%08x\n",
+ result);
+ if ((result & LC_MBOXIN_DONE) != 0)
+ break;
+ }
+
+ /* Clear the valid bit to signal we're done sending the command */
+ pci_write_config(dev, vsec + m_out, 0, 4);
+
+ cmd->cmd_resp = result & LC_MBOXIN_CMD_MASK;
+ cmd->data_out = result >> LC_MBOXIN_CMD_SHIFT;
+
+ if ((result & LC_MBOXIN_DONE) == 0)
+ return (ETIMEDOUT);
+
+ return (0);
+}
+
+static int
+tb_pcib_pcie2cio_wait(device_t dev, u_int timeout)
+{
+#if 0
+ uint32_t val;
+ int vsec;
+
+ vsec = TB_PCIB_VSEC(dev);
+ do {
+ pci_read_config(dev, vsec + PCIE2CIO_CMD, &val);
+ if ((val & PCIE2CIO_CMD_START) == 0) {
+ if (val & PCIE2CIO_CMD_TIMEOUT)
+ break;
+ return 0;
+ }
+
+ msleep(50);
+ } while (time_before(jiffies, end));
+
+#endif
+ return ETIMEDOUT;
+}
+
+static int
+tb_pcib_pcie2cio_read(device_t dev, u_int space, u_int port, u_int offset,
+ uint32_t *val)
+{
+#if 0
+ uint32_t cmd;
+ int ret, vsec;
+
+ vsec = TB_PCIB_VSEC(dev);
+ if (vsec == -1)
+ return (EOPNOTSUPP);
+
+ cmd = index;
+ cmd |= (port << PCIE2CIO_CMD_PORT_SHIFT) & PCIE2CIO_CMD_PORT_MASK;
+ cmd |= (space << PCIE2CIO_CMD_CS_SHIFT) & PCIE2CIO_CMD_CS_MASK;
+ cmd |= PCIE2CIO_CMD_START;
+ pci_write_config(dev, vsec + PCIE2CIO_CMD, cmd, 4);
+
+ if ((ret = pci2cio_wait_completion(dev, 5000)) != 0)
+ return (ret);
+
+ *val = pci_read_config(dev, vsec + PCIE2CIO_RDDATA, 4);
+#endif
+ return (0);
+}
+
+static int
+tb_pcib_pcie2cio_write(device_t dev, u_int space, u_int port, u_int offset,
+ uint32_t val)
+{
+#if 0
+ uint32_t cmd;
+ int ret, vsec;
+
+ vsec = TB_PCIB_VSEC(dev);
+ if (vsec == -1)
+ return (EOPNOTSUPP);
+
+ pci_write_config(dev, vsec + PCIE2CIO_WRDATA, val, 4);
+
+ cmd = index;
+ cmd |= (port << PCIE2CIO_CMD_PORT_SHIFT) & PCIE2CIO_CMD_PORT_MASK;
+ cmd |= (space << PCIE2CIO_CMD_CS_SHIFT) & PCIE2CIO_CMD_CS_MASK;
+ cmd |= PCIE2CIO_CMD_WRITE | PCIE2CIO_CMD_START;
+ pci_write_config(dev, vsec + PCIE2CIO_CMD, cmd);
+
+#endif
+ return (tb_pcib_pcie2cio_wait(dev, 5000));
+}
+
+/*
+ * The Upstream Facing Port (UFP) in a switch is special, it's the function
+ * that responds to some of the special programming mailboxes. It can't be
+ * differentiated by PCI ID, so a heuristic approach to identifying it is
+ * required.
+ */
+static int
+tb_pcib_find_ufp(device_t dev, device_t *ufp)
+{
+ device_t upstream;
+ struct tb_pcib_softc *sc;
+ uint32_t vsec, val;
+ int error;
+
+ upstream = NULL;
+ sc = device_get_softc(dev);
+ if (sc == NULL)
+ return (EOPNOTSUPP);
+
+ if (TB_IS_UFP(sc)) {
+ upstream = dev;
+ error = 0;
+ goto out;
+ }
+
+ /*
+ * This register is supposed to be filled in on the upstream port
+ * and tells how many downstream ports there are. It doesn't seem
+ * to get filled in on AR host controllers, but is on various
+ * peripherals.
+ */
+ error = pci_find_extcap(dev, PCIZ_VENDOR, &vsec);
+ if (error == 0) {
+ val = pci_read_config(dev, vsec + 0x18, 4);
+ if ((val & 0x1f) > 0) {
+ upstream = dev;
+ goto out;
+ }
+ }
+
+ /*
+ * Since we can't trust that the VSEC register is filled in, the only
+ * other option is to see if we're at the top of the topology, which
+ * implies that we're at the upstream port of the host controller.
+ */
+ error = TB_FIND_UFP(device_get_parent(dev), ufp);
+ if (error == EOPNOTSUPP) {
+ upstream = dev;
+ error = 0;
+ goto out;
+ } else
+ return (error);
+
+out:
+ if (ufp != NULL)
+ *ufp = upstream;
+
+ return (error);
+}
+
+static int
+tb_pcib_get_debug(device_t dev, u_int *debug)
+{
+ struct tb_pcib_softc *sc;
+
+ sc = device_get_softc(dev);
+ if ((sc == NULL) || (debug == NULL))
+ return (EOPNOTSUPP);
+
+ *debug = sc->debug;
+ return (0);
+}
+
+static device_method_t tb_pcib_methods[] = {
+ DEVMETHOD(device_probe, tb_pcib_probe),
+ DEVMETHOD(device_attach, tb_pcib_attach),
+ DEVMETHOD(device_detach, tb_pcib_detach),
+
+ DEVMETHOD(tb_lc_mailbox, tb_pcib_lc_mailbox),
+ DEVMETHOD(tb_pcie2cio_read, tb_pcib_pcie2cio_read),
+ DEVMETHOD(tb_pcie2cio_write, tb_pcib_pcie2cio_write),
+
+ DEVMETHOD(tb_find_ufp, tb_pcib_find_ufp),
+ DEVMETHOD(tb_get_debug, tb_pcib_get_debug),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(tbolt, tb_pcib_driver, tb_pcib_methods,
+ sizeof(struct tb_pcib_softc), pcib_driver);
+DRIVER_MODULE_ORDERED(tb_pcib, pci, tb_pcib_driver,
+ NULL, NULL, SI_ORDER_MIDDLE);
+MODULE_DEPEND(tb_pcib, pci, 1, 1, 1);
+MODULE_PNP_INFO("U16:vendor;U16:device;U16:subvendor;U16:subdevice;U32:#;D:#",
+ pci, tb_pcib, tb_pcib_identifiers, nitems(tb_pcib_identifiers) - 1);
+
+static int
+tb_pci_probe(device_t dev)
+{
+ struct tb_pcib_ident *n;
+
+ if ((n = tb_pcib_find_ident(device_get_parent(dev))) != NULL) {
+ switch (n->flags & TB_GEN_MASK) {
+ case TB_GEN_TB1:
+ device_set_desc(dev, "Thunderbolt 1 Link");
+ break;
+ case TB_GEN_TB2:
+ device_set_desc(dev, "Thunderbolt 2 Link");
+ break;
+ case TB_GEN_TB3:
+ device_set_desc(dev, "Thunderbolt 3 Link");
+ break;
+ case TB_GEN_USB4:
+ device_set_desc(dev, "USB4 Link");
+ break;
+ case TB_GEN_UNK:
+ /* Fallthrough */
+ default:
+ device_set_desc(dev, "Thunderbolt Link");
+ }
+ return (BUS_PROBE_VENDOR);
+ }
+ return (ENXIO);
+}
+
+static int
+tb_pci_attach(device_t dev)
+{
+
+ return (pci_attach(dev));
+}
+
+static int
+tb_pci_detach(device_t dev)
+{
+
+ return (pci_detach(dev));
+}
+
+static device_method_t tb_pci_methods[] = {
+ DEVMETHOD(device_probe, tb_pci_probe),
+ DEVMETHOD(device_attach, tb_pci_attach),
+ DEVMETHOD(device_detach, tb_pci_detach),
+
+ DEVMETHOD(tb_find_ufp, tb_generic_find_ufp),
+ DEVMETHOD(tb_get_debug, tb_generic_get_debug),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(pci, tb_pci_driver, tb_pci_methods, sizeof(struct pci_softc),
+ pci_driver);
+DRIVER_MODULE(tb_pci, pcib, tb_pci_driver, NULL, NULL);
+MODULE_DEPEND(tb_pci, pci, 1, 1, 1);
+MODULE_VERSION(tb_pci, 1);