summaryrefslogtreecommitdiff
path: root/drivers/usb/host
diff options
context:
space:
mode:
authorSang-Hun Lee <sanlee@nvidia.com>2012-01-16 01:29:45 -0800
committerRohan Somvanshi <rsomvanshi@nvidia.com>2012-01-24 11:00:08 -0800
commit0a1f8db5eccd9a894ce515b459882c103a04d617 (patch)
tree032636bf3f761d58015e306a2a223fde3164008a /drivers/usb/host
parentf9a16e84e7064361c4e82ae8a7c7a0c40d22dad5 (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')
-rw-r--r--drivers/usb/host/ehci-tegra.c32
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)