usb: ehci-msm-hsic: replace wakelock with wakeup source
In some cases of system suspend, it's possible a runtime resume may be called on a device if it's already autosuspended (say, in order to set a device- or port-specific feature, such as the remote wakeup setting) just prior to putting it back into low power mode. In this case, if the controller was also auto-suspended it will also be temporarily runtime resumed by the fact that it is an ancestor to said device. The incoming wakelock re-implementation from Google based on wakeup sources breaks this driver in the above scenario since now acquiring a wakelock (which calls __pm_stay_awake) during runtime resume while a simultaneous asynchronous suspend callback of another device is called will cause it to abort the entire suspend attempt. This prompts reconsideration of why a wakelock is needed in this driver. One apparent use is to ensure this controller's runtime suspend is called before system suspend is allowed. In other words, we wish to guarantee the HSIC child devices are suspended before suspending the controller. This can be done by checking that the root hub device is suspended, else we can abort the suspend callback by returning -EBUSY. To handle the cases of interrupts happening during systemwide suspend but after the controller was already suspended (and thus its runtime PM disabled), call the pm_stay_awake() to denote a wakeup event. This will cause the suspend to abort as well as block it from happening until the interrupt can be serviced--after the controller resumes. Change-Id: I4fab47c54f7d57065833d96261de08dcdd7238dc Signed-off-by: Jack Pham <jackp@codeaurora.org>
This commit is contained in:
@@ -27,7 +27,6 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/wakelock.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
@@ -57,14 +56,13 @@ struct msm_hsic_hcd {
|
||||
struct clk *phy_clk;
|
||||
struct clk *cal_clk;
|
||||
struct regulator *hsic_vddcx;
|
||||
bool async_int;
|
||||
atomic_t in_lpm;
|
||||
struct wake_lock wlock;
|
||||
int peripheral_status_irq;
|
||||
int wakeup_irq;
|
||||
int wakeup_gpio;
|
||||
bool wakeup_irq_enabled;
|
||||
atomic_t pm_usage_cnt;
|
||||
bool irq_enabled;
|
||||
bool async_int;
|
||||
uint32_t bus_perf_client;
|
||||
uint32_t wakeup_int_cnt;
|
||||
enum usb_vdd_type vdd_type;
|
||||
@@ -599,13 +597,15 @@ static int msm_hsic_suspend(struct msm_hsic_hcd *mehci)
|
||||
|
||||
disable_irq(hcd->irq);
|
||||
|
||||
/* make sure we don't race against a remote wakeup */
|
||||
if (test_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags) ||
|
||||
/* make sure we don't race against the root hub being resumed */
|
||||
if (HCD_RH_RUNNING(hcd) || HCD_WAKEUP_PENDING(hcd) ||
|
||||
readl_relaxed(USB_PORTSC) & PORT_RESUME) {
|
||||
dev_dbg(mehci->dev, "wakeup pending, aborting suspend\n");
|
||||
dev_warn(mehci->dev, "%s: Root hub is not suspended\n",
|
||||
__func__);
|
||||
enable_irq(hcd->irq);
|
||||
return -EBUSY;
|
||||
}
|
||||
mehci->irq_enabled = false;
|
||||
|
||||
/*
|
||||
* PHY may take some time or even fail to enter into low power
|
||||
@@ -666,14 +666,13 @@ static int msm_hsic_suspend(struct msm_hsic_hcd *mehci)
|
||||
}
|
||||
|
||||
atomic_set(&mehci->in_lpm, 1);
|
||||
mehci->irq_enabled = true;
|
||||
enable_irq(hcd->irq);
|
||||
|
||||
mehci->wakeup_irq_enabled = 1;
|
||||
enable_irq_wake(mehci->wakeup_irq);
|
||||
enable_irq(mehci->wakeup_irq);
|
||||
|
||||
wake_unlock(&mehci->wlock);
|
||||
|
||||
dev_info(mehci->dev, "HSIC-USB in low power mode\n");
|
||||
|
||||
return 0;
|
||||
@@ -697,8 +696,6 @@ static int msm_hsic_resume(struct msm_hsic_hcd *mehci)
|
||||
mehci->wakeup_irq_enabled = 0;
|
||||
}
|
||||
|
||||
wake_lock(&mehci->wlock);
|
||||
|
||||
if (mehci->bus_perf_client && debug_bus_voting_enabled) {
|
||||
ret = msm_bus_scale_client_update_request(
|
||||
mehci->bus_perf_client, 1);
|
||||
@@ -757,14 +754,15 @@ skip_phy_resume:
|
||||
if (mehci->async_int) {
|
||||
mehci->async_int = false;
|
||||
pm_runtime_put_noidle(mehci->dev);
|
||||
enable_irq(hcd->irq);
|
||||
}
|
||||
|
||||
if (atomic_read(&mehci->pm_usage_cnt)) {
|
||||
atomic_set(&mehci->pm_usage_cnt, 0);
|
||||
pm_runtime_put_noidle(mehci->dev);
|
||||
if (!mehci->irq_enabled) {
|
||||
enable_irq(hcd->irq);
|
||||
mehci->irq_enabled = true;
|
||||
}
|
||||
|
||||
pm_relax(mehci->dev);
|
||||
|
||||
dev_info(mehci->dev, "HSIC-USB exited from low power mode\n");
|
||||
|
||||
return 0;
|
||||
@@ -777,9 +775,11 @@ static irqreturn_t msm_hsic_irq(struct usb_hcd *hcd)
|
||||
|
||||
if (atomic_read(&mehci->in_lpm)) {
|
||||
disable_irq_nosync(hcd->irq);
|
||||
mehci->irq_enabled = false;
|
||||
dev_dbg(mehci->dev, "phy async intr\n");
|
||||
mehci->async_int = true;
|
||||
pm_runtime_get(mehci->dev);
|
||||
pm_stay_awake(mehci->dev);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
@@ -996,7 +996,9 @@ static irqreturn_t msm_hsic_wakeup_irq(int irq, void *data)
|
||||
dev_dbg(mehci->dev, "%s: hsic remote wakeup interrupt cnt: %u\n",
|
||||
__func__, mehci->wakeup_int_cnt);
|
||||
|
||||
wake_lock(&mehci->wlock);
|
||||
mehci->async_int = true;
|
||||
pm_runtime_get(mehci->dev);
|
||||
pm_stay_awake(mehci->dev);
|
||||
|
||||
if (mehci->wakeup_irq_enabled) {
|
||||
mehci->wakeup_irq_enabled = 0;
|
||||
@@ -1004,11 +1006,6 @@ static irqreturn_t msm_hsic_wakeup_irq(int irq, void *data)
|
||||
disable_irq_nosync(irq);
|
||||
}
|
||||
|
||||
if (!atomic_read(&mehci->pm_usage_cnt)) {
|
||||
atomic_set(&mehci->pm_usage_cnt, 1);
|
||||
pm_runtime_get(mehci->dev);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
@@ -1303,9 +1300,8 @@ static int __devinit ehci_hsic_msm_probe(struct platform_device *pdev)
|
||||
goto unconfig_gpio;
|
||||
}
|
||||
|
||||
mehci->irq_enabled = true;
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
wake_lock_init(&mehci->wlock, WAKE_LOCK_SUSPEND, dev_name(&pdev->dev));
|
||||
wake_lock(&mehci->wlock);
|
||||
|
||||
if (mehci->peripheral_status_irq) {
|
||||
ret = request_threaded_irq(mehci->peripheral_status_irq,
|
||||
@@ -1412,7 +1408,6 @@ static int __devexit ehci_hsic_msm_remove(struct platform_device *pdev)
|
||||
msm_hsic_init_vddcx(mehci, 0);
|
||||
|
||||
msm_hsic_init_clocks(mehci, 0);
|
||||
wake_lock_destroy(&mehci->wlock);
|
||||
iounmap(hcd->regs);
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user