summaryrefslogtreecommitdiff
path: root/drivers/usb/host/xhci-tegra.c
diff options
context:
space:
mode:
authorJC Kuo <jckuo@nvidia.com>2013-06-24 20:19:31 +0800
committerBharat Nihalani <bnihalani@nvidia.com>2013-10-18 03:09:08 -0700
commita01002af03a35f9eec725272dd5a173ae5c8c130 (patch)
tree04bc6bc02686dde103d1c16f5e35a64ea071c1a7 /drivers/usb/host/xhci-tegra.c
parent688dea3223cc049f368d113b1890dbd2b0004dab (diff)
xhci: tegra: support loading xusb firmware from file
This commit add the capability of loading Tegra xusb firmware from a firmware file in file system. Two kernel module parameters provides the firmware loading flexibility. 1. "use_bootloader_firmware=Y" driver loads firmware from the bootloader carveout region. 2. "use_bootloader_firmware=N" driver loads firmware from the file specified by "firmware_file" parameter. This example shows how to load firmware from /etc/firmware/xusb_sil_prod_fw insmod /system/lib/modules/xhci-hcd.ko use_bootloader_firmware=N firmware_file=xusb_sil_prod_fw bug 1301430 bug 1381552 Change-Id: I7ff4a86ab56b2724d3a4d17f28fe048e6303b067 Signed-off-by: JC Kuo <jckuo@nvidia.com> Reviewed-on: http://git-master/r/241457 (cherry picked from commit 12c6b61e6af9a2dbfc5d92fcb0032392ed88594b) Signed-off-by: Henry Lin <henryl@nvidia.com> Reviewed-on: http://git-master/r/299351 Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com> Tested-by: Bharat Nihalani <bnihalani@nvidia.com>
Diffstat (limited to 'drivers/usb/host/xhci-tegra.c')
-rw-r--r--drivers/usb/host/xhci-tegra.c88
1 files changed, 84 insertions, 4 deletions
diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
index 16864a7ad9f2..ce2108dc70f1 100644
--- a/drivers/usb/host/xhci-tegra.c
+++ b/drivers/usb/host/xhci-tegra.c
@@ -35,7 +35,7 @@
#include <linux/usb/otg.h>
#include <linux/clk/tegra.h>
#include <linux/tegra-powergate.h>
-
+#include <linux/firmware.h>
#include <mach/tegra_usb_pad_ctrl.h>
#include <mach/tegra_usb_pmc.h>
#include <mach/pm_domains.h>
@@ -418,6 +418,20 @@ static struct tegra_usb_pmc_data pmc_hsic_data[XUSB_HSIC_COUNT];
static void save_ctle_context(struct tegra_xhci_hcd *tegra,
u8 port) __attribute__ ((unused));
+static bool use_bootloader_firmware = true;
+module_param(use_bootloader_firmware, bool, S_IRUGO);
+MODULE_PARM_DESC(use_bootloader_firmware, "take bootloader initialized firmware");
+
+#define FIRMWARE_FILE "xusb_sil_rel_fw"
+static char *firmware_file = FIRMWARE_FILE;
+#define FIRMWARE_FILE_HELP \
+ "used to specify firmware file of Tegra XHCI host controller. "\
+ "This takes effect only if \"use_bootloader_firmware\" is \"N\". " \
+ "Default value is \"" FIRMWARE_FILE "\"."
+
+module_param(firmware_file, charp, S_IRUGO);
+MODULE_PARM_DESC(firmware_file, FIRMWARE_FILE_HELP);
+
/* functions */
static inline struct tegra_xhci_hcd *hcd_to_tegra_xhci(struct usb_hcd *hcd)
{
@@ -3755,14 +3769,78 @@ static void deinit_bootloader_firmware(struct tegra_xhci_hcd *tegra)
memset(&tegra->firmware, 0, sizeof(tegra->firmware));
}
+static int init_filesystem_firmware(struct tegra_xhci_hcd *tegra)
+{
+ struct platform_device *pdev = tegra->pdev;
+ const struct firmware *fw;
+ struct cfgtbl *fw_cfgtbl;
+ size_t fw_size;
+ void *fw_data;
+ dma_addr_t fw_dma;
+ int ret;
+
+ ret = request_firmware(&fw, firmware_file, &pdev->dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "request_firmware failed %d\n", ret);
+ return ret;
+ }
+
+ fw_cfgtbl = (struct cfgtbl *) fw->data;
+ fw_size = fw_cfgtbl->fwimg_len;
+ dev_info(&pdev->dev, "Firmware File: %s (%d Bytes)\n",
+ firmware_file, fw_size);
+
+ fw_data = dma_alloc_coherent(&pdev->dev, fw_size,
+ &fw_dma, GFP_KERNEL);
+ if (!fw_data) {
+ dev_err(&pdev->dev, "%s: dma_alloc_coherent failed\n",
+ __func__);
+ ret = -ENOMEM;
+ goto error_release_firmware;
+ }
+
+ memcpy(fw_data, fw->data, fw_size);
+ dev_info(&pdev->dev, "Firmware DMA Memory: dma 0x%p mapped 0x%p (%d Bytes)\n",
+ (void *) fw_dma, fw_data, fw_size);
+
+ release_firmware(fw);
+
+ /* all set and ready to go */
+ tegra->firmware.data = fw_data;
+ tegra->firmware.dma = fw_dma;
+ tegra->firmware.size = fw_size;
+ return 0;
+
+error_release_firmware:
+ release_firmware(fw);
+ return ret;
+}
+
+static void deinit_filesystem_firmware(struct tegra_xhci_hcd *tegra)
+{
+ struct platform_device *pdev = tegra->pdev;
+
+ if (tegra->firmware.data) {
+ dma_free_coherent(&pdev->dev, tegra->firmware.size,
+ tegra->firmware.data, tegra->firmware.dma);
+ }
+
+ memset(&tegra->firmware, 0, sizeof(tegra->firmware));
+}
static int init_firmware(struct tegra_xhci_hcd *tegra)
{
- return init_bootloader_firmware(tegra);
+ if (use_bootloader_firmware)
+ return init_bootloader_firmware(tegra);
+ else
+ return init_filesystem_firmware(tegra);
}
static void deinit_firmware(struct tegra_xhci_hcd *tegra)
{
- deinit_bootloader_firmware(tegra);
+ if (use_bootloader_firmware)
+ return deinit_bootloader_firmware(tegra);
+ else
+ return deinit_filesystem_firmware(tegra);
}
static int tegra_enable_xusb_clk(struct tegra_xhci_hcd *tegra,
@@ -4166,7 +4244,7 @@ static int tegra_xhci_probe(struct platform_device *pdev)
if (ret < 0) {
dev_err(&pdev->dev, "failed to init firmware\n");
ret = -ENODEV;
- goto err_deinit_usb2_clocks;
+ goto err_deinit_firmware_log;
}
ret = load_firmware(tegra, true /* do reset ARU */);
@@ -4309,6 +4387,8 @@ err_put_usb2_hcd:
usb_put_hcd(hcd);
err_deinit_firmware:
deinit_firmware(tegra);
+err_deinit_firmware_log:
+ fw_log_deinit(tegra);
err_deinit_usb2_clocks:
tegra_usb2_clocks_deinit(tegra);
err_deinit_tegra_xusb_regulator: