summaryrefslogtreecommitdiff
path: root/drivers/usb/host
diff options
context:
space:
mode:
authorSeshendra Gadagottu <sgadagottu@nvidia.com>2011-11-17 14:35:53 +0530
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:50:14 -0800
commita0cdadeff1d8d18fefcea24bd9e824195d85a2a7 (patch)
treecc460a48d218345cc7ec7900e997d29d4a2afc0f /drivers/usb/host
parent021ca15c7915383e86d1066398b8cd094f4a10ca (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.c126
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);
}