Cannot build xhci-hcd and xhci-tegra as kernel module

We need to download a firmware binary into a pcie usb host controller.
Currently the xhci-hcd (builtin) module is blocking for about 30 seconds and firmware download always fails until the driver gives up initializing a not-programmed chip.

I am trying to build xhci-hcd as a kernel module because I need to delay loading the module until the firmware has finished downloading into the chip which take around 3 seconds.

when I try to build the xhci-hcd as a module the build fails because some functions are seemingly not defined:

CONFIG_USB_XHCI_HCD=m
CONFIG_USB_XHCI_PCI=m
CONFIG_USB_XHCI_PLATFORM=m
CONFIG_USB_XHCI_TEGRA=m
ERROR: "xhci_enable_usb3_lpm_timeout" [drivers/usb/host/xhci-tegra.ko] undefined!
ERROR: "xhci_update_device" [drivers/usb/host/xhci-tegra.ko] undefined!
ERROR: "xhci_alloc_dev" [drivers/usb/host/xhci-tegra.ko] undefined!
ERROR: "xhci_free_dev" [drivers/usb/host/xhci-tegra.ko] undefined!
ERROR: "xhci_urb_enqueue" [drivers/usb/host/xhci-tegra.ko] undefined!
ERROR: "pm_qos_update_request_timeout" [drivers/usb/host/xhci-tegra.ko] undefined!

these are included in the code through “xhci.h” and should be present.
Any ideas how to properly build xhci-tegra as module?

Did you make this additional configuration starting with the current configuration from the working system’s “/proc/config.gz”? Did you make the edit through a tool such as “make nconfig” or “make menuconfig”? If not, then dependencies won’t be automatically set.

For time being, if it is ok to disable ‘CONFIG_USB_XHCI_TEGRA’ config, please do so.
Meanwhile we’ll provide a solution.

Hi stfl,
We have checked and confirmed we don’t support this case.

I’ve managed to build it as a module already by exporting some symbols from other modules.

diff --git a/arch/arm64/configs/tegra18_defconfig b/arch/arm64/configs/tegra18_defconfig
index a4974a1..3377fef 100644
--- a/arch/arm64/configs/tegra18_defconfig
+++ b/arch/arm64/configs/tegra18_defconfig
@@ -474,9 +474,9 @@ CONFIG_HID_ZYDACRON=y
 CONFIG_USB_OTG=y
 CONFIG_USB=y
 CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
-CONFIG_USB_XHCI_HCD=y
-CONFIG_USB_XHCI_PLATFORM=y
-CONFIG_USB_XHCI_TEGRA=y
+CONFIG_USB_XHCI_HCD=m
+CONFIG_USB_XHCI_PLATFORM=m
+CONFIG_USB_XHCI_TEGRA=m
 CONFIG_USB_STORAGE=y
 CONFIG_USB_SERIAL=y
 CONFIG_USB_SERIAL_CP210X=y
diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c
index fd74130..06869dd 100644
--- a/drivers/pci/host/pci-tegra.c
+++ b/drivers/pci/host/pci-tegra.c
@@ -2050,6 +2050,7 @@ static void tegra_pcie_port_enable(struct tegra_pcie_port *port)
 
 	afi_writel(port->pcie, value, ctrl);
 
+  mdelay(100);
 	tegra_pcie_port_reset(port);
 
 	/* On platforms where MXM is not directly connected to Tegra root port,
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index bb47885..936c22a 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -23,7 +23,6 @@ ifneq ($(CONFIG_USB_XHCI_RCAR), )
 	xhci-plat-hcd-y		+= xhci-rcar.o
 endif
 
-obj-$(CONFIG_USB_XHCI_TEGRA)    += xhci-tegra.o
 obj-$(CONFIG_USB_WHCI_HCD)	+= whci/
 
 ifneq ($(CONFIG_USB), )
@@ -77,3 +76,6 @@ obj-$(CONFIG_USB_HCD_BCMA)	+= bcma-hcd.o
 obj-$(CONFIG_USB_HCD_SSB)	+= ssb-hcd.o
 obj-$(CONFIG_USB_FOTG210_HCD)	+= fotg210-hcd.o
 obj-$(CONFIG_USB_MAX3421_HCD)	+= max3421-hcd.o
+
+obj-$(CONFIG_USB_XHCI_TEGRA)    += xhci-tegra.o
+CFLAGS_xhci-tegra.o := -I$(src)
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 58e7dfa..af2c895 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -1457,6 +1457,7 @@ free_priv:
 	spin_unlock_irqrestore(&xhci->lock, flags);
 	return ret;
 }
+EXPORT_SYMBOL(xhci_urb_enqueue);
 
 /* Get the right ring for the given URB.
  * If the endpoint supports streams, boundary check the URB's stream ID.
@@ -3674,6 +3675,7 @@ void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev)
 	 * associated with the slot.  XXX Can free sleep?
 	 */
 }
+EXPORT_SYMBOL(xhci_free_dev);
 
 /*
  * Checks if we have enough host controller resources for the default control
@@ -3789,6 +3791,7 @@ disable_slot:
 	spin_unlock_irqrestore(&xhci->lock, flags);
 	return 0;
 }
+EXPORT_SYMBOL(xhci_alloc_dev);
 
 /*
  * Issue an Address Device command and optionally send a corresponding
@@ -4284,6 +4287,7 @@ int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
 
 	return 0;
 }
+EXPORT_SYMBOL(xhci_update_device);
 
 /*---------------------- USB 3.0 Link PM functions ------------------------*/
 
@@ -4696,6 +4700,7 @@ int xhci_enable_usb3_lpm_timeout(struct usb_hcd *hcd,
 		return ret;
 	return hub_encoded_timeout;
 }
+EXPORT_SYMBOL(xhci_enable_usb3_lpm_timeout);
 
 int xhci_disable_usb3_lpm_timeout(struct usb_hcd *hcd,
 			struct usb_device *udev, enum usb3_link_state state)
diff --git a/kernel/power/qos.c b/kernel/power/qos.c
index cfbbf19..d63daec 100644
--- a/kernel/power/qos.c
+++ b/kernel/power/qos.c
@@ -1130,6 +1130,7 @@ void pm_qos_update_request_timeout(struct pm_qos_request *req, s32 new_value,
 
 	schedule_delayed_work(&req->work, usecs_to_jiffies(timeout_us));
 }
+EXPORT_SYMBOL(pm_qos_update_request_timeout);
 
 /**
  * pm_qos_remove_request - modifies an existing qos request

Thanks for the sharing.