diff options
author | Sang-Hun Lee <sanlee@nvidia.com> | 2012-01-16 01:29:45 -0800 |
---|---|---|
committer | Rohan Somvanshi <rsomvanshi@nvidia.com> | 2012-01-24 11:00:08 -0800 |
commit | 0a1f8db5eccd9a894ce515b459882c103a04d617 (patch) | |
tree | 032636bf3f761d58015e306a2a223fde3164008a /drivers/usb/host/ehci-tegra.c | |
parent | f9a16e84e7064361c4e82ae8a7c7a0c40d22dad5 (diff) |
usb: host: tegra: avoid disabling clock while in use
Problem description:
- ehci is accessed while holding a different lock from the bus
disable logic, such as tegra_ehci_shutdown and
tegra_ehci_bus_suspend
- the access to disabled clock happens in tegra_ehci_irq and
tegra_ehci_hub_control
Fix description:
- tegra_ehci_hub_control: Acquire tegra_ehci_hcd_mutex for the
duration of tegra_ehci_hub_control to ensure tegra_ehci_shutdown
or tegra_ehci_bus_suspend does not disable the clock while ehci is
being used
- tegra_ehci_irq: Disable the interrupt and wait for interrupt
handlers to finish when the clock is about to be disabled
Bug 923414
Signed-off-by: Sang-Hun Lee <sanlee@nvidia.com>
Reviewed-on: http://git-master/r/75534
(cherry picked from commit 4a9ec3021a7515a82fa1511e92113ac22afcd17a)
Reviewed-on: http://git-master/r/74062
Change-Id: I758921f441f2b5af71f57ce08dfd6786621b5cbf
Signed-off-by: Varun Wadekar <vwadekar@nvidia.com>
Reviewed-on: http://git-master/r/76824
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Sang-Hun Lee <sanlee@nvidia.com>
Diffstat (limited to 'drivers/usb/host/ehci-tegra.c')
-rw-r--r-- | drivers/usb/host/ehci-tegra.c | 32 |
1 files changed, 31 insertions, 1 deletions
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 1dbfc4aa1981..dfd4c479c366 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -238,9 +238,11 @@ static int tegra_ehci_hub_control( bool hsic = false; bool do_post_resume = false; + mutex_lock(&tegra->tegra_ehci_hcd_mutex); if (!tegra->host_resumed) { if (buf) memset (buf, 0, wLength); + mutex_unlock(&tegra->tegra_ehci_hcd_mutex); return retval; } @@ -458,9 +460,12 @@ static int tegra_ehci_hub_control( spin_unlock_irqrestore(&ehci->lock, flags); /* Handle the hub control events here */ - return ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength); + retval = ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength); + mutex_unlock(&tegra->tegra_ehci_hcd_mutex); + return retval; done: spin_unlock_irqrestore(&ehci->lock, flags); + mutex_unlock(&tegra->tegra_ehci_hcd_mutex); return retval; } @@ -700,11 +705,35 @@ restart: } #endif +/* + * Disable PHY clock valid interrupts and wait for the interrupt handler to + * finish. + * + * Requires a lock on tegra_ehci_hcd_mutex + * Must not be called with a lock on ehci->lock + */ +static void tegra_ehci_disable_phy_interrupt(struct usb_hcd *hcd) { + struct tegra_ehci_hcd *tegra; + u32 val; + if (hcd->irq >= 0) { + tegra = dev_get_drvdata(hcd->self.controller); + if (tegra->phy->hotplug) { + /* Disable PHY clock valid interrupts */ + val = readl(hcd->regs + TEGRA_USB_SUSP_CTRL_OFFSET); + val &= ~TEGRA_USB_PHY_CLK_VALID_INT_ENB; + writel(val , (hcd->regs + TEGRA_USB_SUSP_CTRL_OFFSET)); + } + /* Wait for the interrupt handler to finish */ + synchronize_irq(hcd->irq); + } +} + static void tegra_ehci_shutdown(struct usb_hcd *hcd) { struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); mutex_lock(&tegra->tegra_ehci_hcd_mutex); + tegra_ehci_disable_phy_interrupt(hcd); /* ehci_shutdown touches the USB controller registers, make sure * controller has clocks to it */ if (!tegra->host_resumed) @@ -780,6 +809,7 @@ static int tegra_ehci_bus_suspend(struct usb_hcd *hcd) int error_status = 0; mutex_lock(&tegra->tegra_ehci_hcd_mutex); + tegra_ehci_disable_phy_interrupt(hcd); /* ehci_shutdown touches the USB controller registers, make sure * controller has clocks to it */ if (!tegra->host_resumed) |