diff options
author | Seshendra Gadagottu <sgadagottu@nvidia.com> | 2011-11-17 14:35:53 +0530 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2011-11-30 21:50:14 -0800 |
commit | a0cdadeff1d8d18fefcea24bd9e824195d85a2a7 (patch) | |
tree | cc460a48d218345cc7ec7900e997d29d4a2afc0f /drivers/usb/host | |
parent | 021ca15c7915383e86d1066398b8cd094f4a10ca (diff) |
tegra: usb: tegra: clk_timer changes with EHCI power on/off
Disabled the power management clk_timer on EHCI power off.
Enabled the clk_timer on EHCI power on after hcd on.
This is done to avoid kernel panic during flight mode on/off.
Bug 881388
Reviewed-on: http://git-master/r/61093
(cherry picked from commit 5e36d4788f0bbfcd7c6965390f0e1a977b491a23)
Change-Id: I5f957aaa5b1c949cd395da92c83a70fdd551a6a1
Reviewed-on: http://git-master/r/65168
Reviewed-by: Lokesh Pathak <lpathak@nvidia.com>
Tested-by: Lokesh Pathak <lpathak@nvidia.com>
Rebase-Id: R7ad855dc3a9840ec4ab6b7a0bbbca24cd1ebc730
Diffstat (limited to 'drivers/usb/host')
-rw-r--r-- | drivers/usb/host/ehci-tegra.c | 126 |
1 files changed, 72 insertions, 54 deletions
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index ac13286bfe3c..a1a4c7c1093a 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -875,6 +875,58 @@ static void tegra_hsic_connection_work(struct work_struct *work) return; } +void clk_timer_callback(unsigned long data) +{ + struct tegra_ehci_hcd *tegra = (struct tegra_ehci_hcd*) data; + unsigned long flags; + + if (!timer_pending(&tegra->clk_timer)) { + spin_lock_irqsave(&tegra->ehci->lock, flags); + tegra->timer_event = 1; + spin_unlock_irqrestore(&tegra->ehci->lock, flags); + schedule_work(&tegra->clk_timer_work); + } +} + +static void clk_timer_work_handler(struct work_struct* clk_timer_work) { + struct tegra_ehci_hcd *tegra = container_of(clk_timer_work, + struct tegra_ehci_hcd, clk_timer_work); + int ret; + unsigned long flags; + bool clock_enabled, timer_event; + + spin_lock_irqsave(&tegra->ehci->lock, flags); + clock_enabled = tegra->clock_enabled; + timer_event = tegra->timer_event; + spin_unlock_irqrestore(&tegra->ehci->lock, flags); + + if (timer_event) { + spin_lock_irqsave(&tegra->ehci->lock, flags); + tegra->clock_enabled = 0; + tegra->timer_event = 0; + spin_unlock_irqrestore(&tegra->ehci->lock, flags); + clk_disable(tegra->emc_clk); + clk_disable(tegra->sclk_clk); + return; + } + + if ((!clock_enabled)) { + ret = mod_timer(&tegra->clk_timer, jiffies + msecs_to_jiffies(2000)); + if (ret) + pr_err("tegra_ehci_urb_enqueue timer modify failed \n"); + clk_enable(tegra->emc_clk); + clk_enable(tegra->sclk_clk); + spin_lock_irqsave(&tegra->ehci->lock, flags); + tegra->clock_enabled = 1; + spin_unlock_irqrestore(&tegra->ehci->lock, flags); + } else { + if (timer_pending(&tegra->clk_timer)) { + mod_timer_pending (&tegra->clk_timer, jiffies + + msecs_to_jiffies(2000)); + } + } +} + #ifdef CONFIG_USB_EHCI_ONOFF_FEATURE /* Stored ehci handle for hsic insatnce */ struct usb_hcd *ehci_handle; @@ -895,23 +947,41 @@ static ssize_t store_ehci_power(struct device *dev, int retval; struct tegra_ehci_hcd *tegra = dev_get_drvdata(dev); struct usb_hcd *hcd = ehci_to_hcd(tegra->ehci); + bool clock_enabled; + unsigned long flags; if (sscanf(buf, "%d", &power_on) != 1) return -EINVAL; if (power_on == 0 && ehci_handle != NULL) { + del_timer_sync(&tegra->clk_timer); + spin_lock_irqsave(&tegra->ehci->lock, flags); + clock_enabled = tegra->clock_enabled; + spin_unlock_irqrestore(&tegra->ehci->lock, flags); + if (clock_enabled) { + spin_lock_irqsave(&tegra->ehci->lock, flags); + tegra->clock_enabled = 0; + spin_unlock_irqrestore(&tegra->ehci->lock, flags); + clk_disable(tegra->emc_clk); + clk_disable(tegra->sclk_clk); + } usb_remove_hcd(hcd); tegra_ehci_power_down(hcd, false); ehci_handle = NULL; } else if (power_on == 1) { - if (ehci_handle) + if (ehci_handle) { + del_timer_sync(&tegra->clk_timer); usb_remove_hcd(hcd); + } tegra_ehci_power_up(hcd, false); retval = usb_add_hcd(hcd, ehci_tegra_irq, IRQF_DISABLED | IRQF_SHARED); if (retval < 0) printk(KERN_ERR "power_on error\n"); ehci_handle = hcd; + init_timer(&tegra->clk_timer); + tegra->clk_timer.function = clk_timer_callback; + tegra->clk_timer.data = (unsigned long) tegra; } return count; @@ -934,58 +1004,6 @@ static inline void remove_ehci_sys_file(struct ehci_hcd *ehci) #endif -void clk_timer_callback(unsigned long data) -{ - struct tegra_ehci_hcd *tegra = (struct tegra_ehci_hcd*) data; - unsigned long flags; - - if (!timer_pending(&tegra->clk_timer)) { - spin_lock_irqsave(&tegra->ehci->lock, flags); - tegra->timer_event = 1; - spin_unlock_irqrestore(&tegra->ehci->lock, flags); - schedule_work(&tegra->clk_timer_work); - } -} - -static void clk_timer_work_handler(struct work_struct* clk_timer_work) { - struct tegra_ehci_hcd *tegra = container_of(clk_timer_work, - struct tegra_ehci_hcd, clk_timer_work); - int ret; - unsigned long flags; - bool clock_enabled, timer_event; - - spin_lock_irqsave(&tegra->ehci->lock, flags); - clock_enabled = tegra->clock_enabled; - timer_event = tegra->timer_event; - spin_unlock_irqrestore(&tegra->ehci->lock, flags); - - if (timer_event) { - spin_lock_irqsave(&tegra->ehci->lock, flags); - tegra->clock_enabled = 0; - tegra->timer_event = 0; - spin_unlock_irqrestore(&tegra->ehci->lock, flags); - clk_disable(tegra->emc_clk); - clk_disable(tegra->sclk_clk); - return; - } - - if ((!clock_enabled)) { - ret = mod_timer(&tegra->clk_timer, jiffies + msecs_to_jiffies(2000)); - if (ret) - pr_err("tegra_ehci_urb_enqueue timer modify failed \n"); - clk_enable(tegra->emc_clk); - clk_enable(tegra->sclk_clk); - spin_lock_irqsave(&tegra->ehci->lock, flags); - tegra->clock_enabled = 1; - spin_unlock_irqrestore(&tegra->ehci->lock, flags); - } else { - if (timer_pending(&tegra->clk_timer)) { - mod_timer_pending (&tegra->clk_timer, jiffies - + msecs_to_jiffies(2000)); - } - } -} - static int tegra_ehci_urb_enqueue ( struct usb_hcd *hcd, struct urb *urb, @@ -1310,7 +1328,7 @@ static int tegra_ehci_remove(struct platform_device *pdev) clk_disable(tegra->clk); clk_put(tegra->clk); - if (tegra->clock_enabled){ + if (tegra->clock_enabled) { clk_disable(tegra->sclk_clk); clk_disable(tegra->emc_clk); } |