summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
authorJC Kuo <jckuo@nvidia.com>2013-06-24 20:19:31 +0800
committerHarshada Kale <hkale@nvidia.com>2013-07-19 06:05:53 -0700
commit12c6b61e6af9a2dbfc5d92fcb0032392ed88594b (patch)
tree1c3cb8f9bff4df1ca269f2573f863f390fc1e8a8 /drivers/usb
parent1fb658156251fdd5927a7d7b2df0cb0a775ee3e6 (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 Change-Id: I7ff4a86ab56b2724d3a4d17f28fe048e6303b067 Signed-off-by: JC Kuo <jckuo@nvidia.com> Reviewed-on: http://git-master/r/241457 Reviewed-by: Harshada Kale <hkale@nvidia.com> Tested-by: Harshada Kale <hkale@nvidia.com>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/host/xhci-tegra.c87
1 files changed, 84 insertions, 3 deletions
diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
index 6206fa4bfec6..67906a31b40e 100644
--- a/drivers/usb/host/xhci-tegra.c
+++ b/drivers/usb/host/xhci-tegra.c
@@ -31,6 +31,7 @@
#include <linux/vmalloc.h>
#include <linux/debugfs.h>
#include <linux/kthread.h>
+#include <linux/firmware.h>
#include <mach/powergate.h>
#include <mach/clk.h>
#include <mach/tegra_usb_pad_ctrl.h>
@@ -296,6 +297,20 @@ struct tegra_xhci_hcd {
static struct tegra_usb_pmc_data pmc_data;
+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)
{
@@ -3131,14 +3146,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);
}
/* TODO: we have to refine error handling in tegra_xhci_probe() */
@@ -3257,7 +3336,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 */);
@@ -3405,6 +3484,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: