usb: host: xhci-tegra: Use CNR as firmware ready indicator
The Falcon CPU state is a suboptimal indicator for firmware readiness, since the Falcon can be in a running state if the firmware is handling port state changes or running other tasks. Instead, the driver should check the STS_CNR bit to determine whether or not the firmware has been successfully loaded and is ready for XHCI operation. Based on work by JC Kuo <jckuo@nvidia.com>. Signed-off-by: Thierry Reding <treding@nvidia.com> Link: https://lore.kernel.org/r/20191206140653.2085561-5-thierry.reding@gmail.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
ec12ac10c9
commit
482ba7a6b4
|
@ -830,10 +830,10 @@ static int tegra_xusb_request_firmware(struct tegra_xusb *tegra)
|
||||||
static int tegra_xusb_load_firmware(struct tegra_xusb *tegra)
|
static int tegra_xusb_load_firmware(struct tegra_xusb *tegra)
|
||||||
{
|
{
|
||||||
unsigned int code_tag_blocks, code_size_blocks, code_blocks;
|
unsigned int code_tag_blocks, code_size_blocks, code_blocks;
|
||||||
|
struct xhci_cap_regs __iomem *cap = tegra->regs;
|
||||||
struct tegra_xusb_fw_header *header;
|
struct tegra_xusb_fw_header *header;
|
||||||
struct xhci_cap_regs __iomem *cap;
|
|
||||||
struct xhci_op_regs __iomem *op;
|
|
||||||
struct device *dev = tegra->dev;
|
struct device *dev = tegra->dev;
|
||||||
|
struct xhci_op_regs __iomem *op;
|
||||||
unsigned long timeout;
|
unsigned long timeout;
|
||||||
time64_t timestamp;
|
time64_t timestamp;
|
||||||
struct tm time;
|
struct tm time;
|
||||||
|
@ -842,6 +842,7 @@ static int tegra_xusb_load_firmware(struct tegra_xusb *tegra)
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
header = (struct tegra_xusb_fw_header *)tegra->fw.virt;
|
header = (struct tegra_xusb_fw_header *)tegra->fw.virt;
|
||||||
|
op = tegra->regs + HC_LENGTH(readl(&cap->hc_capbase));
|
||||||
|
|
||||||
if (csb_readl(tegra, XUSB_CSB_MP_ILOAD_BASE_LO) != 0) {
|
if (csb_readl(tegra, XUSB_CSB_MP_ILOAD_BASE_LO) != 0) {
|
||||||
dev_info(dev, "Firmware already loaded, Falcon state %#x\n",
|
dev_info(dev, "Firmware already loaded, Falcon state %#x\n",
|
||||||
|
@ -911,21 +912,23 @@ static int tegra_xusb_load_firmware(struct tegra_xusb *tegra)
|
||||||
csb_writel(tegra, le32_to_cpu(header->boot_codetag),
|
csb_writel(tegra, le32_to_cpu(header->boot_codetag),
|
||||||
XUSB_FALC_BOOTVEC);
|
XUSB_FALC_BOOTVEC);
|
||||||
|
|
||||||
/* Boot Falcon CPU and wait for it to enter the STOPPED (idle) state. */
|
/* Boot Falcon CPU and wait for USBSTS_CNR to get cleared. */
|
||||||
timeout = jiffies + msecs_to_jiffies(5);
|
|
||||||
|
|
||||||
csb_writel(tegra, CPUCTL_STARTCPU, XUSB_FALC_CPUCTL);
|
csb_writel(tegra, CPUCTL_STARTCPU, XUSB_FALC_CPUCTL);
|
||||||
|
|
||||||
while (time_before(jiffies, timeout)) {
|
timeout = jiffies + msecs_to_jiffies(200);
|
||||||
if (csb_readl(tegra, XUSB_FALC_CPUCTL) == CPUCTL_STATE_STOPPED)
|
|
||||||
|
do {
|
||||||
|
value = readl(&op->status);
|
||||||
|
if ((value & STS_CNR) == 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
usleep_range(100, 200);
|
usleep_range(1000, 2000);
|
||||||
}
|
} while (time_is_after_jiffies(timeout));
|
||||||
|
|
||||||
if (csb_readl(tegra, XUSB_FALC_CPUCTL) != CPUCTL_STATE_STOPPED) {
|
value = readl(&op->status);
|
||||||
dev_err(dev, "Falcon failed to start, state: %#x\n",
|
if (value & STS_CNR) {
|
||||||
csb_readl(tegra, XUSB_FALC_CPUCTL));
|
value = csb_readl(tegra, XUSB_FALC_CPUCTL);
|
||||||
|
dev_err(dev, "XHCI controller not read: %#010x\n", value);
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue