EHCI: HSIC: Handle remote wake using GPIO
HSIC h/w cannot support auto resume when MDM wakes up
APQ using remote wakeup during vdd minimization. This
can result in phy lockup or device disconnection as HSIC
io pads are not designed to work properly during vdd
minimization. Instead, let MDM indicate remote wakeup using
GPIO for APQ HSIC to resume the bus.
CRs-Fixed: 355600
Change-Id: I9c7a78a1389e11484096243473da44c23c07a3ff
Signed-off-by: Hemant Kumar <hemantk@codeaurora.org>
diff --git a/arch/arm/mach-msm/board-8064-gpiomux.c b/arch/arm/mach-msm/board-8064-gpiomux.c
index 48d1129..8008cff 100644
--- a/arch/arm/mach-msm/board-8064-gpiomux.c
+++ b/arch/arm/mach-msm/board-8064-gpiomux.c
@@ -507,6 +507,19 @@
.dir = GPIOMUX_OUT_LOW,
};
+static struct gpiomux_setting hsic_wakeup_act_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_DOWN,
+ .dir = GPIOMUX_IN,
+};
+
+static struct gpiomux_setting hsic_wakeup_sus_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+ .dir = GPIOMUX_IN,
+};
static struct msm_gpiomux_config apq8064_hsic_configs[] = {
{
@@ -523,6 +536,13 @@
[GPIOMUX_SUSPENDED] = &hsic_sus_cfg,
},
},
+ {
+ .gpio = 47, /* wake up */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &hsic_wakeup_act_cfg,
+ [GPIOMUX_SUSPENDED] = &hsic_wakeup_sus_cfg,
+ },
+ },
};
#endif
diff --git a/arch/arm/mach-msm/devices-8064.c b/arch/arm/mach-msm/devices-8064.c
index 6131590..8f65d60 100644
--- a/arch/arm/mach-msm/devices-8064.c
+++ b/arch/arm/mach-msm/devices-8064.c
@@ -771,10 +771,10 @@
.flags = IORESOURCE_IRQ,
},
{
- .start = MSM_GPIO_TO_INT(88),
- .end = MSM_GPIO_TO_INT(88),
- .name = "wakeup_irq",
- .flags = IORESOURCE_IRQ,
+ .start = 47,
+ .end = 47,
+ .name = "wakeup",
+ .flags = IORESOURCE_IO,
},
};
diff --git a/drivers/usb/host/ehci-msm-hsic.c b/drivers/usb/host/ehci-msm-hsic.c
index 82373e2..95627fb 100644
--- a/drivers/usb/host/ehci-msm-hsic.c
+++ b/drivers/usb/host/ehci-msm-hsic.c
@@ -56,8 +56,11 @@
struct wake_lock wlock;
int peripheral_status_irq;
int wakeup_irq;
+ int wakeup_gpio;
bool wakeup_irq_enabled;
+ atomic_t pm_usage_cnt;
uint32_t bus_perf_client;
+ uint32_t wakeup_int_cnt;
};
static bool debug_bus_voting_enabled = true;
@@ -186,9 +189,20 @@
goto free_strobe;
}
+ if (mehci->wakeup_gpio) {
+ rc = gpio_request(mehci->wakeup_gpio, "HSIC_WAKEUP_GPIO");
+ if (rc < 0) {
+ dev_err(mehci->dev, "gpio request failed for HSIC WAKEUP\n");
+ goto free_data;
+ }
+ }
+
return 0;
free_gpio:
+ if (mehci->wakeup_gpio)
+ gpio_free(mehci->wakeup_gpio);
+free_data:
gpio_free(pdata->data);
free_strobe:
gpio_free(pdata->strobe);
@@ -321,6 +335,9 @@
ulpi_write(mehci, 0xA9, 0x30);
}
+ /*disable auto resume*/
+ ulpi_write(mehci, ULPI_IFC_CTRL_AUTORESUME, ULPI_CLR(ULPI_IFC_CTRL));
+
return 0;
}
@@ -408,6 +425,11 @@
atomic_set(&mehci->in_lpm, 1);
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");
@@ -426,6 +448,12 @@
return 0;
}
+ if (mehci->wakeup_irq_enabled) {
+ disable_irq_wake(mehci->wakeup_irq);
+ disable_irq_nosync(mehci->wakeup_irq);
+ mehci->wakeup_irq_enabled = 0;
+ }
+
wake_lock(&mehci->wlock);
if (mehci->bus_perf_client && debug_bus_voting_enabled) {
@@ -478,6 +506,8 @@
skip_phy_resume:
+ usb_hcd_resume_root_hub(hcd);
+
atomic_set(&mehci->in_lpm, 0);
if (mehci->async_int) {
@@ -486,6 +516,11 @@
enable_irq(hcd->irq);
}
+ if (atomic_read(&mehci->pm_usage_cnt)) {
+ atomic_set(&mehci->pm_usage_cnt, 0);
+ pm_runtime_put_noidle(mehci->dev);
+ }
+
dev_info(mehci->dev, "HSIC-USB exited from low power mode\n");
return 0;
@@ -687,7 +722,11 @@
{
struct msm_hsic_hcd *mehci = data;
- dev_dbg(mehci->dev, "%s: hsic remote wakeup interrupt\n", __func__);
+ mehci->wakeup_int_cnt++;
+ dev_dbg(mehci->dev, "%s: hsic remote wakeup interrupt cnt: %u\n",
+ __func__, mehci->wakeup_int_cnt);
+
+ wake_lock(&mehci->wlock);
if (mehci->wakeup_irq_enabled) {
mehci->wakeup_irq_enabled = 0;
@@ -695,6 +734,11 @@
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;
}
@@ -751,6 +795,27 @@
.release = single_release,
};
+static int ehci_hsic_msm_wakeup_cnt_show(struct seq_file *s, void *unused)
+{
+ struct msm_hsic_hcd *mehci = s->private;
+
+ seq_printf(s, "%u\n", mehci->wakeup_int_cnt);
+
+ return 0;
+}
+
+static int ehci_hsic_msm_wakeup_cnt_open(struct inode *inode, struct file *f)
+{
+ return single_open(f, ehci_hsic_msm_wakeup_cnt_show, inode->i_private);
+}
+
+const struct file_operations ehci_hsic_msm_wakeup_cnt_fops = {
+ .open = ehci_hsic_msm_wakeup_cnt_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
static struct dentry *ehci_hsic_msm_dbg_root;
static int ehci_hsic_msm_debugfs_init(struct msm_hsic_hcd *mehci)
{
@@ -771,6 +836,16 @@
return -ENODEV;
}
+ ehci_hsic_msm_dentry = debugfs_create_file("wakeup_cnt",
+ S_IRUGO,
+ ehci_hsic_msm_dbg_root, mehci,
+ &ehci_hsic_msm_wakeup_cnt_fops);
+
+ if (!ehci_hsic_msm_dentry) {
+ debugfs_remove_recursive(ehci_hsic_msm_dbg_root);
+ return -ENODEV;
+ }
+
return 0;
}
@@ -836,6 +911,13 @@
if (res)
mehci->peripheral_status_irq = res->start;
+ res = platform_get_resource_byname(pdev, IORESOURCE_IO, "wakeup");
+ if (res) {
+ mehci->wakeup_gpio = res->start;
+ mehci->wakeup_irq = MSM_GPIO_TO_INT(res->start);
+ dev_dbg(mehci->dev, "wakeup_irq: %d\n", mehci->wakeup_irq);
+ }
+
ret = msm_hsic_init_clocks(mehci, 1);
if (ret) {
dev_err(&pdev->dev, "unable to initialize clocks\n");
@@ -878,12 +960,9 @@
}
/* configure wakeup irq */
- ret = platform_get_irq(pdev, 2);
- if (ret > 0) {
- mehci->wakeup_irq = ret;
- dev_dbg(&pdev->dev, "wakeup_irq: %d\n", mehci->wakeup_irq);
+ if (mehci->wakeup_irq) {
ret = request_irq(mehci->wakeup_irq, msm_hsic_wakeup_irq,
- IRQF_TRIGGER_LOW,
+ IRQF_TRIGGER_HIGH,
"msm_hsic_wakeup", mehci);
if (!ret) {
disable_irq_nosync(mehci->wakeup_irq);
@@ -999,39 +1078,15 @@
return ret;
}
-static int msm_hsic_pm_suspend_noirq(struct device *dev)
-{
- struct usb_hcd *hcd = dev_get_drvdata(dev);
- struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd);
-
- dev_dbg(dev, "ehci-msm-hsic PM suspend_noirq\n");
-
- if (device_may_wakeup(dev) && !mehci->wakeup_irq_enabled) {
- enable_irq(mehci->wakeup_irq);
- enable_irq_wake(mehci->wakeup_irq);
- mehci->wakeup_irq_enabled = 1;
- }
-
- return 0;
-}
-
static int msm_hsic_pm_resume(struct device *dev)
{
int ret;
struct usb_hcd *hcd = dev_get_drvdata(dev);
struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd);
- dev_dbg(dev, "ehci-msm-hsic PM resume\n");
-
if (device_may_wakeup(dev))
disable_irq_wake(hcd->irq);
- if (mehci->wakeup_irq_enabled) {
- mehci->wakeup_irq_enabled = 0;
- disable_irq_wake(mehci->wakeup_irq);
- disable_irq_nosync(mehci->wakeup_irq);
- }
-
ret = msm_hsic_resume(mehci);
if (ret)
return ret;
@@ -1074,7 +1129,6 @@
#ifdef CONFIG_PM
static const struct dev_pm_ops msm_hsic_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(msm_hsic_pm_suspend, msm_hsic_pm_resume)
- .suspend_noirq = msm_hsic_pm_suspend_noirq,
SET_RUNTIME_PM_OPS(msm_hsic_runtime_suspend, msm_hsic_runtime_resume,
msm_hsic_runtime_idle)
};