1*e241d98eSKonrad Dybcio // SPDX-License-Identifier: GPL-2.0-only 2*e241d98eSKonrad Dybcio /* 3*e241d98eSKonrad Dybcio * Thunderbolt driver - PCI NHI driver 4*e241d98eSKonrad Dybcio * 5*e241d98eSKonrad Dybcio * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com> 6*e241d98eSKonrad Dybcio * Copyright (C) 2018, Intel Corporation 7*e241d98eSKonrad Dybcio */ 8*e241d98eSKonrad Dybcio 9*e241d98eSKonrad Dybcio #include <linux/pm_runtime.h> 10*e241d98eSKonrad Dybcio #include <linux/slab.h> 11*e241d98eSKonrad Dybcio #include <linux/errno.h> 12*e241d98eSKonrad Dybcio #include <linux/pci.h> 13*e241d98eSKonrad Dybcio #include <linux/dma-mapping.h> 14*e241d98eSKonrad Dybcio #include <linux/interrupt.h> 15*e241d98eSKonrad Dybcio #include <linux/iommu.h> 16*e241d98eSKonrad Dybcio #include <linux/module.h> 17*e241d98eSKonrad Dybcio #include <linux/delay.h> 18*e241d98eSKonrad Dybcio #include <linux/property.h> 19*e241d98eSKonrad Dybcio #include <linux/string_helpers.h> 20*e241d98eSKonrad Dybcio #include <linux/suspend.h> 21*e241d98eSKonrad Dybcio 22*e241d98eSKonrad Dybcio #include "nhi.h" 23*e241d98eSKonrad Dybcio #include "nhi_regs.h" 24*e241d98eSKonrad Dybcio #include "tb.h" 25*e241d98eSKonrad Dybcio 26*e241d98eSKonrad Dybcio /** 27*e241d98eSKonrad Dybcio * struct tb_nhi_pci - NHI device connected over PCIe 28*e241d98eSKonrad Dybcio * @nhi: NHI device 29*e241d98eSKonrad Dybcio * @msix_ida: Used to allocate MSI-X vectors for rings 30*e241d98eSKonrad Dybcio */ 31*e241d98eSKonrad Dybcio struct tb_nhi_pci { 32*e241d98eSKonrad Dybcio struct tb_nhi nhi; 33*e241d98eSKonrad Dybcio struct ida msix_ida; 34*e241d98eSKonrad Dybcio }; 35*e241d98eSKonrad Dybcio 36*e241d98eSKonrad Dybcio static inline struct tb_nhi_pci *nhi_to_pci(struct tb_nhi *nhi) 37*e241d98eSKonrad Dybcio { 38*e241d98eSKonrad Dybcio return container_of(nhi, struct tb_nhi_pci, nhi); 39*e241d98eSKonrad Dybcio } 40*e241d98eSKonrad Dybcio 41*e241d98eSKonrad Dybcio static void nhi_pci_check_quirks(struct tb_nhi_pci *nhi_pci) 42*e241d98eSKonrad Dybcio { 43*e241d98eSKonrad Dybcio struct tb_nhi *nhi = &nhi_pci->nhi; 44*e241d98eSKonrad Dybcio struct pci_dev *pdev = to_pci_dev(nhi->dev); 45*e241d98eSKonrad Dybcio 46*e241d98eSKonrad Dybcio if (pdev->vendor == PCI_VENDOR_ID_INTEL) { 47*e241d98eSKonrad Dybcio /* 48*e241d98eSKonrad Dybcio * Intel hardware supports auto clear of the interrupt 49*e241d98eSKonrad Dybcio * status register right after interrupt is being 50*e241d98eSKonrad Dybcio * issued. 51*e241d98eSKonrad Dybcio */ 52*e241d98eSKonrad Dybcio nhi->quirks |= QUIRK_AUTO_CLEAR_INT; 53*e241d98eSKonrad Dybcio 54*e241d98eSKonrad Dybcio switch (pdev->device) { 55*e241d98eSKonrad Dybcio case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI: 56*e241d98eSKonrad Dybcio case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI: 57*e241d98eSKonrad Dybcio /* 58*e241d98eSKonrad Dybcio * Falcon Ridge controller needs the end-to-end 59*e241d98eSKonrad Dybcio * flow control workaround to avoid losing Rx 60*e241d98eSKonrad Dybcio * packets when RING_FLAG_E2E is set. 61*e241d98eSKonrad Dybcio */ 62*e241d98eSKonrad Dybcio nhi->quirks |= QUIRK_E2E; 63*e241d98eSKonrad Dybcio break; 64*e241d98eSKonrad Dybcio } 65*e241d98eSKonrad Dybcio } 66*e241d98eSKonrad Dybcio } 67*e241d98eSKonrad Dybcio 68*e241d98eSKonrad Dybcio static int nhi_pci_check_iommu_pdev(struct pci_dev *pdev, void *data) 69*e241d98eSKonrad Dybcio { 70*e241d98eSKonrad Dybcio if (!pdev->external_facing || 71*e241d98eSKonrad Dybcio !device_iommu_capable(&pdev->dev, IOMMU_CAP_PRE_BOOT_PROTECTION)) 72*e241d98eSKonrad Dybcio return 0; 73*e241d98eSKonrad Dybcio *(bool *)data = true; 74*e241d98eSKonrad Dybcio return 1; /* Stop walking */ 75*e241d98eSKonrad Dybcio } 76*e241d98eSKonrad Dybcio 77*e241d98eSKonrad Dybcio static void nhi_pci_check_iommu(struct tb_nhi_pci *nhi_pci) 78*e241d98eSKonrad Dybcio { 79*e241d98eSKonrad Dybcio struct tb_nhi *nhi = &nhi_pci->nhi; 80*e241d98eSKonrad Dybcio struct pci_dev *pdev = to_pci_dev(nhi->dev); 81*e241d98eSKonrad Dybcio struct pci_bus *bus = pdev->bus; 82*e241d98eSKonrad Dybcio bool port_ok = false; 83*e241d98eSKonrad Dybcio 84*e241d98eSKonrad Dybcio /* 85*e241d98eSKonrad Dybcio * Ideally what we'd do here is grab every PCI device that 86*e241d98eSKonrad Dybcio * represents a tunnelling adapter for this NHI and check their 87*e241d98eSKonrad Dybcio * status directly, but unfortunately USB4 seems to make it 88*e241d98eSKonrad Dybcio * obnoxiously difficult to reliably make any correlation. 89*e241d98eSKonrad Dybcio * 90*e241d98eSKonrad Dybcio * So for now we'll have to bodge it... Hoping that the system 91*e241d98eSKonrad Dybcio * is at least sane enough that an adapter is in the same PCI 92*e241d98eSKonrad Dybcio * segment as its NHI, if we can find *something* on that segment 93*e241d98eSKonrad Dybcio * which meets the requirements for Kernel DMA Protection, we'll 94*e241d98eSKonrad Dybcio * take that to imply that firmware is aware and has (hopefully) 95*e241d98eSKonrad Dybcio * done the right thing in general. We need to know that the PCI 96*e241d98eSKonrad Dybcio * layer has seen the ExternalFacingPort property which will then 97*e241d98eSKonrad Dybcio * inform the IOMMU layer to enforce the complete "untrusted DMA" 98*e241d98eSKonrad Dybcio * flow, but also that the IOMMU driver itself can be trusted not 99*e241d98eSKonrad Dybcio * to have been subverted by a pre-boot DMA attack. 100*e241d98eSKonrad Dybcio */ 101*e241d98eSKonrad Dybcio while (bus->parent) 102*e241d98eSKonrad Dybcio bus = bus->parent; 103*e241d98eSKonrad Dybcio 104*e241d98eSKonrad Dybcio pci_walk_bus(bus, nhi_pci_check_iommu_pdev, &port_ok); 105*e241d98eSKonrad Dybcio 106*e241d98eSKonrad Dybcio nhi->iommu_dma_protection = port_ok; 107*e241d98eSKonrad Dybcio dev_dbg(nhi->dev, "IOMMU DMA protection is %s\n", 108*e241d98eSKonrad Dybcio str_enabled_disabled(port_ok)); 109*e241d98eSKonrad Dybcio } 110*e241d98eSKonrad Dybcio 111*e241d98eSKonrad Dybcio static int nhi_pci_init_msi(struct tb_nhi *nhi) 112*e241d98eSKonrad Dybcio { 113*e241d98eSKonrad Dybcio struct tb_nhi_pci *nhi_pci = nhi_to_pci(nhi); 114*e241d98eSKonrad Dybcio struct pci_dev *pdev = to_pci_dev(nhi->dev); 115*e241d98eSKonrad Dybcio struct device *dev = &pdev->dev; 116*e241d98eSKonrad Dybcio int res, irq, nvec; 117*e241d98eSKonrad Dybcio 118*e241d98eSKonrad Dybcio ida_init(&nhi_pci->msix_ida); 119*e241d98eSKonrad Dybcio 120*e241d98eSKonrad Dybcio /* 121*e241d98eSKonrad Dybcio * The NHI has 16 MSI-X vectors or a single MSI. We first try to 122*e241d98eSKonrad Dybcio * get all MSI-X vectors and if we succeed, each ring will have 123*e241d98eSKonrad Dybcio * one MSI-X. If for some reason that does not work out, we 124*e241d98eSKonrad Dybcio * fallback to a single MSI. 125*e241d98eSKonrad Dybcio */ 126*e241d98eSKonrad Dybcio nvec = pci_alloc_irq_vectors(pdev, MSIX_MIN_VECS, MSIX_MAX_VECS, 127*e241d98eSKonrad Dybcio PCI_IRQ_MSIX); 128*e241d98eSKonrad Dybcio if (nvec < 0) { 129*e241d98eSKonrad Dybcio nvec = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI); 130*e241d98eSKonrad Dybcio if (nvec < 0) 131*e241d98eSKonrad Dybcio return nvec; 132*e241d98eSKonrad Dybcio 133*e241d98eSKonrad Dybcio INIT_WORK(&nhi->interrupt_work, nhi_interrupt_work); 134*e241d98eSKonrad Dybcio 135*e241d98eSKonrad Dybcio irq = pci_irq_vector(pdev, 0); 136*e241d98eSKonrad Dybcio if (irq < 0) 137*e241d98eSKonrad Dybcio return irq; 138*e241d98eSKonrad Dybcio 139*e241d98eSKonrad Dybcio res = devm_request_irq(&pdev->dev, irq, nhi_msi, 140*e241d98eSKonrad Dybcio IRQF_NO_SUSPEND, "thunderbolt", nhi); 141*e241d98eSKonrad Dybcio if (res) 142*e241d98eSKonrad Dybcio return dev_err_probe(dev, res, "request_irq failed, aborting\n"); 143*e241d98eSKonrad Dybcio } 144*e241d98eSKonrad Dybcio 145*e241d98eSKonrad Dybcio return 0; 146*e241d98eSKonrad Dybcio } 147*e241d98eSKonrad Dybcio 148*e241d98eSKonrad Dybcio static bool nhi_pci_imr_valid(struct pci_dev *pdev) 149*e241d98eSKonrad Dybcio { 150*e241d98eSKonrad Dybcio u8 val; 151*e241d98eSKonrad Dybcio 152*e241d98eSKonrad Dybcio if (!device_property_read_u8(&pdev->dev, "IMR_VALID", &val)) 153*e241d98eSKonrad Dybcio return !!val; 154*e241d98eSKonrad Dybcio 155*e241d98eSKonrad Dybcio return true; 156*e241d98eSKonrad Dybcio } 157*e241d98eSKonrad Dybcio 158*e241d98eSKonrad Dybcio static void nhi_pci_start_dma_port(struct tb_nhi *nhi) 159*e241d98eSKonrad Dybcio { 160*e241d98eSKonrad Dybcio struct pci_dev *pdev = to_pci_dev(nhi->dev); 161*e241d98eSKonrad Dybcio struct pci_dev *root_port; 162*e241d98eSKonrad Dybcio 163*e241d98eSKonrad Dybcio /* 164*e241d98eSKonrad Dybcio * During host router NVM upgrade we should not allow root port to 165*e241d98eSKonrad Dybcio * go into D3cold because some root ports cannot trigger PME 166*e241d98eSKonrad Dybcio * itself. To be on the safe side keep the root port in D0 during 167*e241d98eSKonrad Dybcio * the whole upgrade process. 168*e241d98eSKonrad Dybcio */ 169*e241d98eSKonrad Dybcio root_port = pcie_find_root_port(pdev); 170*e241d98eSKonrad Dybcio if (root_port) 171*e241d98eSKonrad Dybcio pm_runtime_get_noresume(&root_port->dev); 172*e241d98eSKonrad Dybcio } 173*e241d98eSKonrad Dybcio 174*e241d98eSKonrad Dybcio static void nhi_pci_complete_dma_port(struct tb_nhi *nhi) 175*e241d98eSKonrad Dybcio { 176*e241d98eSKonrad Dybcio struct pci_dev *pdev = to_pci_dev(nhi->dev); 177*e241d98eSKonrad Dybcio struct pci_dev *root_port; 178*e241d98eSKonrad Dybcio 179*e241d98eSKonrad Dybcio root_port = pcie_find_root_port(pdev); 180*e241d98eSKonrad Dybcio if (root_port) 181*e241d98eSKonrad Dybcio pm_runtime_put(&root_port->dev); 182*e241d98eSKonrad Dybcio } 183*e241d98eSKonrad Dybcio 184*e241d98eSKonrad Dybcio static int nhi_pci_ring_request_msix(struct tb_ring *ring, bool no_suspend) 185*e241d98eSKonrad Dybcio { 186*e241d98eSKonrad Dybcio struct tb_nhi *nhi = ring->nhi; 187*e241d98eSKonrad Dybcio struct tb_nhi_pci *nhi_pci = nhi_to_pci(nhi); 188*e241d98eSKonrad Dybcio struct pci_dev *pdev = to_pci_dev(nhi->dev); 189*e241d98eSKonrad Dybcio unsigned long irqflags; 190*e241d98eSKonrad Dybcio int ret; 191*e241d98eSKonrad Dybcio 192*e241d98eSKonrad Dybcio if (!pdev->msix_enabled) 193*e241d98eSKonrad Dybcio return 0; 194*e241d98eSKonrad Dybcio 195*e241d98eSKonrad Dybcio ret = ida_alloc_max(&nhi_pci->msix_ida, MSIX_MAX_VECS - 1, GFP_KERNEL); 196*e241d98eSKonrad Dybcio if (ret < 0) 197*e241d98eSKonrad Dybcio return ret; 198*e241d98eSKonrad Dybcio 199*e241d98eSKonrad Dybcio ring->vector = ret; 200*e241d98eSKonrad Dybcio 201*e241d98eSKonrad Dybcio ret = pci_irq_vector(pdev, ring->vector); 202*e241d98eSKonrad Dybcio if (ret < 0) 203*e241d98eSKonrad Dybcio goto err_ida_remove; 204*e241d98eSKonrad Dybcio 205*e241d98eSKonrad Dybcio ring->irq = ret; 206*e241d98eSKonrad Dybcio 207*e241d98eSKonrad Dybcio irqflags = no_suspend ? IRQF_NO_SUSPEND : 0; 208*e241d98eSKonrad Dybcio ret = request_irq(ring->irq, ring_msix, irqflags, "thunderbolt", ring); 209*e241d98eSKonrad Dybcio if (ret) 210*e241d98eSKonrad Dybcio goto err_ida_remove; 211*e241d98eSKonrad Dybcio 212*e241d98eSKonrad Dybcio return 0; 213*e241d98eSKonrad Dybcio 214*e241d98eSKonrad Dybcio err_ida_remove: 215*e241d98eSKonrad Dybcio ida_free(&nhi_pci->msix_ida, ring->vector); 216*e241d98eSKonrad Dybcio 217*e241d98eSKonrad Dybcio return ret; 218*e241d98eSKonrad Dybcio } 219*e241d98eSKonrad Dybcio 220*e241d98eSKonrad Dybcio static void nhi_pci_ring_release_msix(struct tb_ring *ring) 221*e241d98eSKonrad Dybcio { 222*e241d98eSKonrad Dybcio struct tb_nhi_pci *nhi_pci = nhi_to_pci(ring->nhi); 223*e241d98eSKonrad Dybcio 224*e241d98eSKonrad Dybcio if (ring->irq <= 0) 225*e241d98eSKonrad Dybcio return; 226*e241d98eSKonrad Dybcio 227*e241d98eSKonrad Dybcio free_irq(ring->irq, ring); 228*e241d98eSKonrad Dybcio ida_free(&nhi_pci->msix_ida, ring->vector); 229*e241d98eSKonrad Dybcio ring->vector = 0; 230*e241d98eSKonrad Dybcio ring->irq = 0; 231*e241d98eSKonrad Dybcio } 232*e241d98eSKonrad Dybcio 233*e241d98eSKonrad Dybcio static void nhi_pci_shutdown(struct tb_nhi *nhi) 234*e241d98eSKonrad Dybcio { 235*e241d98eSKonrad Dybcio struct tb_nhi_pci *nhi_pci = nhi_to_pci(nhi); 236*e241d98eSKonrad Dybcio struct pci_dev *pdev = to_pci_dev(nhi->dev); 237*e241d98eSKonrad Dybcio 238*e241d98eSKonrad Dybcio /* 239*e241d98eSKonrad Dybcio * We have to release the irq before calling flush_work. Otherwise an 240*e241d98eSKonrad Dybcio * already executing IRQ handler could call schedule_work again. 241*e241d98eSKonrad Dybcio */ 242*e241d98eSKonrad Dybcio if (!pdev->msix_enabled) { 243*e241d98eSKonrad Dybcio devm_free_irq(nhi->dev, pdev->irq, nhi); 244*e241d98eSKonrad Dybcio flush_work(&nhi->interrupt_work); 245*e241d98eSKonrad Dybcio } 246*e241d98eSKonrad Dybcio ida_destroy(&nhi_pci->msix_ida); 247*e241d98eSKonrad Dybcio } 248*e241d98eSKonrad Dybcio 249*e241d98eSKonrad Dybcio static bool nhi_pci_is_present(struct tb_nhi *nhi) 250*e241d98eSKonrad Dybcio { 251*e241d98eSKonrad Dybcio return pci_device_is_present(to_pci_dev(nhi->dev)); 252*e241d98eSKonrad Dybcio } 253*e241d98eSKonrad Dybcio 254*e241d98eSKonrad Dybcio static const struct tb_nhi_ops pci_nhi_default_ops = { 255*e241d98eSKonrad Dybcio .pre_nvm_auth = nhi_pci_start_dma_port, 256*e241d98eSKonrad Dybcio .post_nvm_auth = nhi_pci_complete_dma_port, 257*e241d98eSKonrad Dybcio .request_ring_irq = nhi_pci_ring_request_msix, 258*e241d98eSKonrad Dybcio .release_ring_irq = nhi_pci_ring_release_msix, 259*e241d98eSKonrad Dybcio .shutdown = nhi_pci_shutdown, 260*e241d98eSKonrad Dybcio .is_present = nhi_pci_is_present, 261*e241d98eSKonrad Dybcio .init_interrupts = nhi_pci_init_msi, 262*e241d98eSKonrad Dybcio }; 263*e241d98eSKonrad Dybcio 264*e241d98eSKonrad Dybcio /* Ice Lake specific NHI operations */ 265*e241d98eSKonrad Dybcio 266*e241d98eSKonrad Dybcio #define ICL_LC_MAILBOX_TIMEOUT 500 /* ms */ 267*e241d98eSKonrad Dybcio 268*e241d98eSKonrad Dybcio static int check_for_device(struct device *dev, void *data) 269*e241d98eSKonrad Dybcio { 270*e241d98eSKonrad Dybcio return tb_is_switch(dev); 271*e241d98eSKonrad Dybcio } 272*e241d98eSKonrad Dybcio 273*e241d98eSKonrad Dybcio static bool icl_nhi_is_device_connected(struct tb_nhi *nhi) 274*e241d98eSKonrad Dybcio { 275*e241d98eSKonrad Dybcio struct tb *tb = dev_get_drvdata(nhi->dev); 276*e241d98eSKonrad Dybcio int ret; 277*e241d98eSKonrad Dybcio 278*e241d98eSKonrad Dybcio ret = device_for_each_child(&tb->root_switch->dev, NULL, 279*e241d98eSKonrad Dybcio check_for_device); 280*e241d98eSKonrad Dybcio return ret > 0; 281*e241d98eSKonrad Dybcio } 282*e241d98eSKonrad Dybcio 283*e241d98eSKonrad Dybcio static int icl_nhi_force_power(struct tb_nhi *nhi, bool power) 284*e241d98eSKonrad Dybcio { 285*e241d98eSKonrad Dybcio struct pci_dev *pdev = to_pci_dev(nhi->dev); 286*e241d98eSKonrad Dybcio u32 vs_cap; 287*e241d98eSKonrad Dybcio 288*e241d98eSKonrad Dybcio /* 289*e241d98eSKonrad Dybcio * The Thunderbolt host controller is present always in Ice Lake 290*e241d98eSKonrad Dybcio * but the firmware may not be loaded and running (depending 291*e241d98eSKonrad Dybcio * whether there is device connected and so on). Each time the 292*e241d98eSKonrad Dybcio * controller is used we need to "Force Power" it first and wait 293*e241d98eSKonrad Dybcio * for the firmware to indicate it is up and running. This "Force 294*e241d98eSKonrad Dybcio * Power" is really not about actually powering on/off the 295*e241d98eSKonrad Dybcio * controller so it is accessible even if "Force Power" is off. 296*e241d98eSKonrad Dybcio * 297*e241d98eSKonrad Dybcio * The actual power management happens inside shared ACPI power 298*e241d98eSKonrad Dybcio * resources using standard ACPI methods. 299*e241d98eSKonrad Dybcio */ 300*e241d98eSKonrad Dybcio pci_read_config_dword(pdev, VS_CAP_22, &vs_cap); 301*e241d98eSKonrad Dybcio if (power) { 302*e241d98eSKonrad Dybcio vs_cap &= ~VS_CAP_22_DMA_DELAY_MASK; 303*e241d98eSKonrad Dybcio vs_cap |= 0x22 << VS_CAP_22_DMA_DELAY_SHIFT; 304*e241d98eSKonrad Dybcio vs_cap |= VS_CAP_22_FORCE_POWER; 305*e241d98eSKonrad Dybcio } else { 306*e241d98eSKonrad Dybcio vs_cap &= ~VS_CAP_22_FORCE_POWER; 307*e241d98eSKonrad Dybcio } 308*e241d98eSKonrad Dybcio pci_write_config_dword(pdev, VS_CAP_22, vs_cap); 309*e241d98eSKonrad Dybcio 310*e241d98eSKonrad Dybcio if (power) { 311*e241d98eSKonrad Dybcio unsigned int retries = 350; 312*e241d98eSKonrad Dybcio u32 val; 313*e241d98eSKonrad Dybcio 314*e241d98eSKonrad Dybcio /* Wait until the firmware tells it is up and running */ 315*e241d98eSKonrad Dybcio do { 316*e241d98eSKonrad Dybcio pci_read_config_dword(pdev, VS_CAP_9, &val); 317*e241d98eSKonrad Dybcio if (val & VS_CAP_9_FW_READY) 318*e241d98eSKonrad Dybcio return 0; 319*e241d98eSKonrad Dybcio usleep_range(3000, 3100); 320*e241d98eSKonrad Dybcio } while (--retries); 321*e241d98eSKonrad Dybcio 322*e241d98eSKonrad Dybcio return -ETIMEDOUT; 323*e241d98eSKonrad Dybcio } 324*e241d98eSKonrad Dybcio 325*e241d98eSKonrad Dybcio return 0; 326*e241d98eSKonrad Dybcio } 327*e241d98eSKonrad Dybcio 328*e241d98eSKonrad Dybcio static void icl_nhi_lc_mailbox_cmd(struct tb_nhi *nhi, enum icl_lc_mailbox_cmd cmd) 329*e241d98eSKonrad Dybcio { 330*e241d98eSKonrad Dybcio struct pci_dev *pdev = to_pci_dev(nhi->dev); 331*e241d98eSKonrad Dybcio u32 data; 332*e241d98eSKonrad Dybcio 333*e241d98eSKonrad Dybcio data = (cmd << VS_CAP_19_CMD_SHIFT) & VS_CAP_19_CMD_MASK; 334*e241d98eSKonrad Dybcio pci_write_config_dword(pdev, VS_CAP_19, data | VS_CAP_19_VALID); 335*e241d98eSKonrad Dybcio } 336*e241d98eSKonrad Dybcio 337*e241d98eSKonrad Dybcio static int icl_nhi_lc_mailbox_cmd_complete(struct tb_nhi *nhi, int timeout) 338*e241d98eSKonrad Dybcio { 339*e241d98eSKonrad Dybcio struct pci_dev *pdev = to_pci_dev(nhi->dev); 340*e241d98eSKonrad Dybcio unsigned long end; 341*e241d98eSKonrad Dybcio u32 data; 342*e241d98eSKonrad Dybcio 343*e241d98eSKonrad Dybcio if (!timeout) 344*e241d98eSKonrad Dybcio goto clear; 345*e241d98eSKonrad Dybcio 346*e241d98eSKonrad Dybcio end = jiffies + msecs_to_jiffies(timeout); 347*e241d98eSKonrad Dybcio do { 348*e241d98eSKonrad Dybcio pci_read_config_dword(pdev, VS_CAP_18, &data); 349*e241d98eSKonrad Dybcio if (data & VS_CAP_18_DONE) 350*e241d98eSKonrad Dybcio goto clear; 351*e241d98eSKonrad Dybcio usleep_range(1000, 1100); 352*e241d98eSKonrad Dybcio } while (time_before(jiffies, end)); 353*e241d98eSKonrad Dybcio 354*e241d98eSKonrad Dybcio return -ETIMEDOUT; 355*e241d98eSKonrad Dybcio 356*e241d98eSKonrad Dybcio clear: 357*e241d98eSKonrad Dybcio /* Clear the valid bit */ 358*e241d98eSKonrad Dybcio pci_write_config_dword(pdev, VS_CAP_19, 0); 359*e241d98eSKonrad Dybcio return 0; 360*e241d98eSKonrad Dybcio } 361*e241d98eSKonrad Dybcio 362*e241d98eSKonrad Dybcio static void icl_nhi_set_ltr(struct tb_nhi *nhi) 363*e241d98eSKonrad Dybcio { 364*e241d98eSKonrad Dybcio struct pci_dev *pdev = to_pci_dev(nhi->dev); 365*e241d98eSKonrad Dybcio u32 max_ltr, ltr; 366*e241d98eSKonrad Dybcio 367*e241d98eSKonrad Dybcio pci_read_config_dword(pdev, VS_CAP_16, &max_ltr); 368*e241d98eSKonrad Dybcio max_ltr &= 0xffff; 369*e241d98eSKonrad Dybcio /* Program the same value for both snoop and no-snoop */ 370*e241d98eSKonrad Dybcio ltr = max_ltr << 16 | max_ltr; 371*e241d98eSKonrad Dybcio pci_write_config_dword(pdev, VS_CAP_15, ltr); 372*e241d98eSKonrad Dybcio } 373*e241d98eSKonrad Dybcio 374*e241d98eSKonrad Dybcio static int icl_nhi_suspend(struct tb_nhi *nhi) 375*e241d98eSKonrad Dybcio { 376*e241d98eSKonrad Dybcio struct tb *tb = dev_get_drvdata(nhi->dev); 377*e241d98eSKonrad Dybcio int ret; 378*e241d98eSKonrad Dybcio 379*e241d98eSKonrad Dybcio if (icl_nhi_is_device_connected(nhi)) 380*e241d98eSKonrad Dybcio return 0; 381*e241d98eSKonrad Dybcio 382*e241d98eSKonrad Dybcio if (tb_switch_is_icm(tb->root_switch)) { 383*e241d98eSKonrad Dybcio /* 384*e241d98eSKonrad Dybcio * If there is no device connected we need to perform 385*e241d98eSKonrad Dybcio * both: a handshake through LC mailbox and force power 386*e241d98eSKonrad Dybcio * down before entering D3. 387*e241d98eSKonrad Dybcio */ 388*e241d98eSKonrad Dybcio icl_nhi_lc_mailbox_cmd(nhi, ICL_LC_PREPARE_FOR_RESET); 389*e241d98eSKonrad Dybcio ret = icl_nhi_lc_mailbox_cmd_complete(nhi, ICL_LC_MAILBOX_TIMEOUT); 390*e241d98eSKonrad Dybcio if (ret) 391*e241d98eSKonrad Dybcio return ret; 392*e241d98eSKonrad Dybcio } 393*e241d98eSKonrad Dybcio 394*e241d98eSKonrad Dybcio return icl_nhi_force_power(nhi, false); 395*e241d98eSKonrad Dybcio } 396*e241d98eSKonrad Dybcio 397*e241d98eSKonrad Dybcio static int icl_nhi_suspend_noirq(struct tb_nhi *nhi, bool wakeup) 398*e241d98eSKonrad Dybcio { 399*e241d98eSKonrad Dybcio struct tb *tb = dev_get_drvdata(nhi->dev); 400*e241d98eSKonrad Dybcio enum icl_lc_mailbox_cmd cmd; 401*e241d98eSKonrad Dybcio 402*e241d98eSKonrad Dybcio if (!pm_suspend_via_firmware()) 403*e241d98eSKonrad Dybcio return icl_nhi_suspend(nhi); 404*e241d98eSKonrad Dybcio 405*e241d98eSKonrad Dybcio if (!tb_switch_is_icm(tb->root_switch)) 406*e241d98eSKonrad Dybcio return 0; 407*e241d98eSKonrad Dybcio 408*e241d98eSKonrad Dybcio cmd = wakeup ? ICL_LC_GO2SX : ICL_LC_GO2SX_NO_WAKE; 409*e241d98eSKonrad Dybcio icl_nhi_lc_mailbox_cmd(nhi, cmd); 410*e241d98eSKonrad Dybcio return icl_nhi_lc_mailbox_cmd_complete(nhi, ICL_LC_MAILBOX_TIMEOUT); 411*e241d98eSKonrad Dybcio } 412*e241d98eSKonrad Dybcio 413*e241d98eSKonrad Dybcio static int icl_nhi_resume(struct tb_nhi *nhi) 414*e241d98eSKonrad Dybcio { 415*e241d98eSKonrad Dybcio int ret; 416*e241d98eSKonrad Dybcio 417*e241d98eSKonrad Dybcio ret = icl_nhi_force_power(nhi, true); 418*e241d98eSKonrad Dybcio if (ret) 419*e241d98eSKonrad Dybcio return ret; 420*e241d98eSKonrad Dybcio 421*e241d98eSKonrad Dybcio icl_nhi_set_ltr(nhi); 422*e241d98eSKonrad Dybcio return 0; 423*e241d98eSKonrad Dybcio } 424*e241d98eSKonrad Dybcio 425*e241d98eSKonrad Dybcio static void icl_nhi_shutdown(struct tb_nhi *nhi) 426*e241d98eSKonrad Dybcio { 427*e241d98eSKonrad Dybcio nhi_pci_shutdown(nhi); 428*e241d98eSKonrad Dybcio 429*e241d98eSKonrad Dybcio icl_nhi_force_power(nhi, false); 430*e241d98eSKonrad Dybcio } 431*e241d98eSKonrad Dybcio 432*e241d98eSKonrad Dybcio static const struct tb_nhi_ops icl_nhi_ops = { 433*e241d98eSKonrad Dybcio .init = icl_nhi_resume, 434*e241d98eSKonrad Dybcio .suspend_noirq = icl_nhi_suspend_noirq, 435*e241d98eSKonrad Dybcio .resume_noirq = icl_nhi_resume, 436*e241d98eSKonrad Dybcio .runtime_suspend = icl_nhi_suspend, 437*e241d98eSKonrad Dybcio .runtime_resume = icl_nhi_resume, 438*e241d98eSKonrad Dybcio .shutdown = icl_nhi_shutdown, 439*e241d98eSKonrad Dybcio .pre_nvm_auth = nhi_pci_start_dma_port, 440*e241d98eSKonrad Dybcio .post_nvm_auth = nhi_pci_complete_dma_port, 441*e241d98eSKonrad Dybcio .request_ring_irq = nhi_pci_ring_request_msix, 442*e241d98eSKonrad Dybcio .release_ring_irq = nhi_pci_ring_release_msix, 443*e241d98eSKonrad Dybcio .is_present = nhi_pci_is_present, 444*e241d98eSKonrad Dybcio .init_interrupts = nhi_pci_init_msi, 445*e241d98eSKonrad Dybcio }; 446*e241d98eSKonrad Dybcio 447*e241d98eSKonrad Dybcio static int nhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) 448*e241d98eSKonrad Dybcio { 449*e241d98eSKonrad Dybcio struct device *dev = &pdev->dev; 450*e241d98eSKonrad Dybcio struct tb_nhi_pci *nhi_pci; 451*e241d98eSKonrad Dybcio struct tb_nhi *nhi; 452*e241d98eSKonrad Dybcio int res; 453*e241d98eSKonrad Dybcio 454*e241d98eSKonrad Dybcio if (!nhi_pci_imr_valid(pdev)) 455*e241d98eSKonrad Dybcio return dev_err_probe(dev, -ENODEV, "firmware image not valid, aborting\n"); 456*e241d98eSKonrad Dybcio 457*e241d98eSKonrad Dybcio res = pcim_enable_device(pdev); 458*e241d98eSKonrad Dybcio if (res) 459*e241d98eSKonrad Dybcio return dev_err_probe(dev, res, "cannot enable PCI device, aborting\n"); 460*e241d98eSKonrad Dybcio 461*e241d98eSKonrad Dybcio nhi_pci = devm_kzalloc(dev, sizeof(*nhi_pci), GFP_KERNEL); 462*e241d98eSKonrad Dybcio if (!nhi_pci) 463*e241d98eSKonrad Dybcio return -ENOMEM; 464*e241d98eSKonrad Dybcio 465*e241d98eSKonrad Dybcio nhi = &nhi_pci->nhi; 466*e241d98eSKonrad Dybcio nhi->dev = dev; 467*e241d98eSKonrad Dybcio nhi->ops = (const struct tb_nhi_ops *)id->driver_data ?: &pci_nhi_default_ops; 468*e241d98eSKonrad Dybcio 469*e241d98eSKonrad Dybcio nhi->iobase = pcim_iomap_region(pdev, 0, "thunderbolt"); 470*e241d98eSKonrad Dybcio res = PTR_ERR_OR_ZERO(nhi->iobase); 471*e241d98eSKonrad Dybcio if (res) 472*e241d98eSKonrad Dybcio return dev_err_probe(dev, res, "cannot obtain PCI resources, aborting\n"); 473*e241d98eSKonrad Dybcio 474*e241d98eSKonrad Dybcio nhi_pci_check_quirks(nhi_pci); 475*e241d98eSKonrad Dybcio nhi_pci_check_iommu(nhi_pci); 476*e241d98eSKonrad Dybcio 477*e241d98eSKonrad Dybcio pci_set_master(pdev); 478*e241d98eSKonrad Dybcio 479*e241d98eSKonrad Dybcio return nhi_probe(&nhi_pci->nhi); 480*e241d98eSKonrad Dybcio } 481*e241d98eSKonrad Dybcio 482*e241d98eSKonrad Dybcio static void nhi_pci_remove(struct pci_dev *pdev) 483*e241d98eSKonrad Dybcio { 484*e241d98eSKonrad Dybcio struct tb *tb = pci_get_drvdata(pdev); 485*e241d98eSKonrad Dybcio struct tb_nhi *nhi = tb->nhi; 486*e241d98eSKonrad Dybcio 487*e241d98eSKonrad Dybcio pm_runtime_get_sync(&pdev->dev); 488*e241d98eSKonrad Dybcio pm_runtime_dont_use_autosuspend(&pdev->dev); 489*e241d98eSKonrad Dybcio pm_runtime_forbid(&pdev->dev); 490*e241d98eSKonrad Dybcio 491*e241d98eSKonrad Dybcio tb_domain_remove(tb); 492*e241d98eSKonrad Dybcio wait_for_completion(&nhi->domain_released); 493*e241d98eSKonrad Dybcio nhi_shutdown(nhi); 494*e241d98eSKonrad Dybcio } 495*e241d98eSKonrad Dybcio 496*e241d98eSKonrad Dybcio static struct pci_device_id nhi_ids[] = { 497*e241d98eSKonrad Dybcio /* 498*e241d98eSKonrad Dybcio * We have to specify class, the TB bridges use the same device and 499*e241d98eSKonrad Dybcio * vendor (sub)id on gen 1 and gen 2 controllers. 500*e241d98eSKonrad Dybcio */ 501*e241d98eSKonrad Dybcio { 502*e241d98eSKonrad Dybcio .class = PCI_CLASS_SYSTEM_OTHER << 8, .class_mask = ~0, 503*e241d98eSKonrad Dybcio .vendor = PCI_VENDOR_ID_INTEL, 504*e241d98eSKonrad Dybcio .device = PCI_DEVICE_ID_INTEL_LIGHT_RIDGE, 505*e241d98eSKonrad Dybcio .subvendor = 0x2222, .subdevice = 0x1111, 506*e241d98eSKonrad Dybcio }, 507*e241d98eSKonrad Dybcio { 508*e241d98eSKonrad Dybcio .class = PCI_CLASS_SYSTEM_OTHER << 8, .class_mask = ~0, 509*e241d98eSKonrad Dybcio .vendor = PCI_VENDOR_ID_INTEL, 510*e241d98eSKonrad Dybcio .device = PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C, 511*e241d98eSKonrad Dybcio .subvendor = 0x2222, .subdevice = 0x1111, 512*e241d98eSKonrad Dybcio }, 513*e241d98eSKonrad Dybcio { 514*e241d98eSKonrad Dybcio .class = PCI_CLASS_SYSTEM_OTHER << 8, .class_mask = ~0, 515*e241d98eSKonrad Dybcio .vendor = PCI_VENDOR_ID_INTEL, 516*e241d98eSKonrad Dybcio .device = PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI, 517*e241d98eSKonrad Dybcio .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, 518*e241d98eSKonrad Dybcio }, 519*e241d98eSKonrad Dybcio { 520*e241d98eSKonrad Dybcio .class = PCI_CLASS_SYSTEM_OTHER << 8, .class_mask = ~0, 521*e241d98eSKonrad Dybcio .vendor = PCI_VENDOR_ID_INTEL, 522*e241d98eSKonrad Dybcio .device = PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI, 523*e241d98eSKonrad Dybcio .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, 524*e241d98eSKonrad Dybcio }, 525*e241d98eSKonrad Dybcio 526*e241d98eSKonrad Dybcio /* Thunderbolt 3 */ 527*e241d98eSKonrad Dybcio { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_NHI) }, 528*e241d98eSKonrad Dybcio { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_NHI) }, 529*e241d98eSKonrad Dybcio { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_USBONLY_NHI) }, 530*e241d98eSKonrad Dybcio { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_NHI) }, 531*e241d98eSKonrad Dybcio { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_USBONLY_NHI) }, 532*e241d98eSKonrad Dybcio { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_NHI) }, 533*e241d98eSKonrad Dybcio { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_NHI) }, 534*e241d98eSKonrad Dybcio { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_USBONLY_NHI) }, 535*e241d98eSKonrad Dybcio { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_NHI) }, 536*e241d98eSKonrad Dybcio { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_NHI) }, 537*e241d98eSKonrad Dybcio { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICL_NHI0), 538*e241d98eSKonrad Dybcio .driver_data = (kernel_ulong_t)&icl_nhi_ops }, 539*e241d98eSKonrad Dybcio { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICL_NHI1), 540*e241d98eSKonrad Dybcio .driver_data = (kernel_ulong_t)&icl_nhi_ops }, 541*e241d98eSKonrad Dybcio /* Thunderbolt 4 */ 542*e241d98eSKonrad Dybcio { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TGL_NHI0), 543*e241d98eSKonrad Dybcio .driver_data = (kernel_ulong_t)&icl_nhi_ops }, 544*e241d98eSKonrad Dybcio { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TGL_NHI1), 545*e241d98eSKonrad Dybcio .driver_data = (kernel_ulong_t)&icl_nhi_ops }, 546*e241d98eSKonrad Dybcio { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TGL_H_NHI0), 547*e241d98eSKonrad Dybcio .driver_data = (kernel_ulong_t)&icl_nhi_ops }, 548*e241d98eSKonrad Dybcio { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TGL_H_NHI1), 549*e241d98eSKonrad Dybcio .driver_data = (kernel_ulong_t)&icl_nhi_ops }, 550*e241d98eSKonrad Dybcio { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ADL_NHI0), 551*e241d98eSKonrad Dybcio .driver_data = (kernel_ulong_t)&icl_nhi_ops }, 552*e241d98eSKonrad Dybcio { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ADL_NHI1), 553*e241d98eSKonrad Dybcio .driver_data = (kernel_ulong_t)&icl_nhi_ops }, 554*e241d98eSKonrad Dybcio { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_RPL_NHI0), 555*e241d98eSKonrad Dybcio .driver_data = (kernel_ulong_t)&icl_nhi_ops }, 556*e241d98eSKonrad Dybcio { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_RPL_NHI1), 557*e241d98eSKonrad Dybcio .driver_data = (kernel_ulong_t)&icl_nhi_ops }, 558*e241d98eSKonrad Dybcio { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_MTL_M_NHI0), 559*e241d98eSKonrad Dybcio .driver_data = (kernel_ulong_t)&icl_nhi_ops }, 560*e241d98eSKonrad Dybcio { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_MTL_P_NHI0), 561*e241d98eSKonrad Dybcio .driver_data = (kernel_ulong_t)&icl_nhi_ops }, 562*e241d98eSKonrad Dybcio { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_MTL_P_NHI1), 563*e241d98eSKonrad Dybcio .driver_data = (kernel_ulong_t)&icl_nhi_ops }, 564*e241d98eSKonrad Dybcio { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_LNL_NHI0), 565*e241d98eSKonrad Dybcio .driver_data = (kernel_ulong_t)&icl_nhi_ops }, 566*e241d98eSKonrad Dybcio { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_LNL_NHI1), 567*e241d98eSKonrad Dybcio .driver_data = (kernel_ulong_t)&icl_nhi_ops }, 568*e241d98eSKonrad Dybcio { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_PTL_M_NHI0), 569*e241d98eSKonrad Dybcio .driver_data = (kernel_ulong_t)&icl_nhi_ops }, 570*e241d98eSKonrad Dybcio { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_PTL_M_NHI1), 571*e241d98eSKonrad Dybcio .driver_data = (kernel_ulong_t)&icl_nhi_ops }, 572*e241d98eSKonrad Dybcio { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_PTL_P_NHI0), 573*e241d98eSKonrad Dybcio .driver_data = (kernel_ulong_t)&icl_nhi_ops }, 574*e241d98eSKonrad Dybcio { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_PTL_P_NHI1), 575*e241d98eSKonrad Dybcio .driver_data = (kernel_ulong_t)&icl_nhi_ops }, 576*e241d98eSKonrad Dybcio { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_WCL_NHI0), 577*e241d98eSKonrad Dybcio .driver_data = (kernel_ulong_t)&icl_nhi_ops }, 578*e241d98eSKonrad Dybcio { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_BARLOW_RIDGE_HOST_80G_NHI) }, 579*e241d98eSKonrad Dybcio { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_BARLOW_RIDGE_HOST_40G_NHI) }, 580*e241d98eSKonrad Dybcio 581*e241d98eSKonrad Dybcio /* Any USB4 compliant host */ 582*e241d98eSKonrad Dybcio { PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_USB4, ~0) }, 583*e241d98eSKonrad Dybcio 584*e241d98eSKonrad Dybcio { 0,} 585*e241d98eSKonrad Dybcio }; 586*e241d98eSKonrad Dybcio 587*e241d98eSKonrad Dybcio MODULE_DEVICE_TABLE(pci, nhi_ids); 588*e241d98eSKonrad Dybcio MODULE_DESCRIPTION("Thunderbolt/USB4 core driver"); 589*e241d98eSKonrad Dybcio MODULE_LICENSE("GPL"); 590*e241d98eSKonrad Dybcio 591*e241d98eSKonrad Dybcio static struct pci_driver nhi_driver = { 592*e241d98eSKonrad Dybcio .name = "thunderbolt", 593*e241d98eSKonrad Dybcio .id_table = nhi_ids, 594*e241d98eSKonrad Dybcio .probe = nhi_pci_probe, 595*e241d98eSKonrad Dybcio .remove = nhi_pci_remove, 596*e241d98eSKonrad Dybcio .shutdown = nhi_pci_remove, 597*e241d98eSKonrad Dybcio .driver.pm = &nhi_pm_ops, 598*e241d98eSKonrad Dybcio }; 599*e241d98eSKonrad Dybcio 600*e241d98eSKonrad Dybcio static int __init nhi_init(void) 601*e241d98eSKonrad Dybcio { 602*e241d98eSKonrad Dybcio int ret; 603*e241d98eSKonrad Dybcio 604*e241d98eSKonrad Dybcio ret = tb_domain_init(); 605*e241d98eSKonrad Dybcio if (ret) 606*e241d98eSKonrad Dybcio return ret; 607*e241d98eSKonrad Dybcio 608*e241d98eSKonrad Dybcio ret = pci_register_driver(&nhi_driver); 609*e241d98eSKonrad Dybcio if (ret) 610*e241d98eSKonrad Dybcio tb_domain_exit(); 611*e241d98eSKonrad Dybcio 612*e241d98eSKonrad Dybcio return ret; 613*e241d98eSKonrad Dybcio } 614*e241d98eSKonrad Dybcio 615*e241d98eSKonrad Dybcio static void __exit nhi_unload(void) 616*e241d98eSKonrad Dybcio { 617*e241d98eSKonrad Dybcio pci_unregister_driver(&nhi_driver); 618*e241d98eSKonrad Dybcio tb_domain_exit(); 619*e241d98eSKonrad Dybcio } 620*e241d98eSKonrad Dybcio 621*e241d98eSKonrad Dybcio rootfs_initcall(nhi_init); 622*e241d98eSKonrad Dybcio module_exit(nhi_unload); 623