diff options
author | JC Kuo <jckuo@nvidia.com> | 2013-06-24 20:19:31 +0800 |
---|---|---|
committer | Bharat Nihalani <bnihalani@nvidia.com> | 2013-10-18 03:09:08 -0700 |
commit | a01002af03a35f9eec725272dd5a173ae5c8c130 (patch) | |
tree | 04bc6bc02686dde103d1c16f5e35a64ea071c1a7 /drivers/usb/host | |
parent | 688dea3223cc049f368d113b1890dbd2b0004dab (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')
-rw-r--r-- | drivers/usb/host/xhci-tegra.c | 88 |
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: |