summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSeshendra Gadagottu <sgadagottu@nvidia.com>2012-01-27 15:23:18 +0530
committerRohan Somvanshi <rsomvanshi@nvidia.com>2012-02-03 05:21:02 -0800
commit0d932c60e6cc9cd1b26b94f0d882fe1716c7dc28 (patch)
tree7cc45d8efb113aa33e6ebcee127a77858c8eaac0
parent59d63497a26dc94612e14c905db6c2368dcdbf43 (diff)
tegra: usb: baseband: CP wakeup during system suspend
baseband_xmm_power driver is updated with suspend/resume noirq callbacks. If any CP wake-up is pending during suspen_noirq callback, then ongoing system suspend will be aborted. With this mechanism, CP wakeup after baseband_xmm_power driver suspend is handled. BUG 904762 Reviewed-on: http://git-master/r/72933 Change-Id: Iae6f638885118c73d6154aad9daefee0cfc8e7d4 Signed-off-by: Seshendra Gadagottu <sgadagottu@nvidia.com> Signed-off-by: Varun Wadekar <vwadekar@nvidia.com> Reviewed-on: http://git-master/r/78435 Reviewed-by: Automatic_Commit_Validation_User
-rw-r--r--arch/arm/mach-tegra/baseband-xmm-power.c137
1 files changed, 116 insertions, 21 deletions
diff --git a/arch/arm/mach-tegra/baseband-xmm-power.c b/arch/arm/mach-tegra/baseband-xmm-power.c
index 17dbc2e94c67..a9bbbac88bf2 100644
--- a/arch/arm/mach-tegra/baseband-xmm-power.c
+++ b/arch/arm/mach-tegra/baseband-xmm-power.c
@@ -27,6 +27,7 @@
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/wakelock.h>
+#include <linux/spinlock.h>
#include <linux/usb.h>
#include <linux/pm_runtime.h>
#include <mach/usb_phy.h>
@@ -67,6 +68,7 @@ static struct usb_device_id xmm_pm_ids[] = {
{}
};
+
static struct gpio tegra_baseband_gpios[] = {
{ -1, GPIOF_OUT_INIT_LOW, "BB_RSTn" },
{ -1, GPIOF_OUT_INIT_LOW, "BB_ON" },
@@ -99,7 +101,13 @@ static bool modem_power_on;
static int power_onoff;
static int reenable_autosuspend;
static struct work_struct autopm_resume_work;
+static bool wakeup_pending;
+static bool modem_sleep_flag;
+static spinlock_t xmm_lock;
+
static void baseband_xmm_power_L2_resume(void);
+static int baseband_xmm_power_driver_handle_resume(
+ struct baseband_power_platform_data *data);
static int baseband_modem_power_on(struct baseband_power_platform_data *data)
{
@@ -140,6 +148,8 @@ static int baseband_xmm_power_on(struct platform_device *device)
/* reset the state machine */
baseband_xmm_powerstate = BBXMM_PS_INIT;
+ modem_sleep_flag = false;
+
if (modem_ver < XMM_MODEM_VER_1130)
ipc_ap_wake_state = IPC_AP_WAKE_INIT1;
else
@@ -185,6 +195,7 @@ static int baseband_xmm_power_off(struct platform_device *device)
{
struct baseband_power_platform_data *data;
int ret;
+ unsigned long flags;
pr_debug("%s {\n", __func__);
@@ -213,6 +224,7 @@ static int baseband_xmm_power_off(struct platform_device *device)
else
pr_err("%s: hsic_unregister is missing\n", __func__);
+
/* set IPC_HSIC_ACTIVE low */
gpio_set_value(baseband_power_driver_data->
modem.xmm.ipc_hsic_active, 0);
@@ -224,7 +236,11 @@ static int baseband_xmm_power_off(struct platform_device *device)
gpio_set_value(data->modem.xmm.bb_rst, 0);
mdelay(1);
+ spin_lock_irqsave(&xmm_lock, flags);
baseband_xmm_powerstate = BBXMM_PS_UNINIT;
+ wakeup_pending = false;
+ modem_sleep_flag = false;
+ spin_unlock_irqrestore(&xmm_lock, flags);
pr_debug("%s }\n", __func__);
return 0;
@@ -264,10 +280,12 @@ static ssize_t baseband_xmm_onoff(struct device *dev,
static DEVICE_ATTR(xmm_onoff, S_IRUSR | S_IWUSR | S_IRGRP,
NULL, baseband_xmm_onoff);
+
void baseband_xmm_set_power_status(unsigned int status)
{
struct baseband_power_platform_data *data = baseband_power_driver_data;
int value = 0;
+ unsigned long flags;
pr_debug("%s\n", __func__);
@@ -276,6 +294,11 @@ void baseband_xmm_set_power_status(unsigned int status)
switch (status) {
case BBXMM_PS_L0:
+ if (modem_sleep_flag) {
+ pr_info("%s Resume from L3 without calling resume"
+ "function\n", __func__);
+ baseband_xmm_power_driver_handle_resume(data);
+ }
pr_info("L0\n");
value = gpio_get_value(data->modem.xmm.ipc_hsic_active);
pr_debug("before L0 ipc_hsic_active=%d\n", value);
@@ -287,14 +310,27 @@ void baseband_xmm_set_power_status(unsigned int status)
modem_power_on = false;
baseband_modem_power_on(data);
}
- wake_lock(&wakelock);
+
+ if (!wake_lock_active(&wakelock))
+ wake_lock(&wakelock);
+
pr_debug("gpio host active high->\n");
break;
case BBXMM_PS_L2:
pr_info("L2\n");
wake_unlock(&wakelock);
+ modem_sleep_flag = true;
break;
case BBXMM_PS_L3:
+ if (baseband_xmm_powerstate == BBXMM_PS_L2TOL0) {
+ if (!data->modem.xmm.ipc_ap_wake) {
+ spin_lock_irqsave(&xmm_lock, flags);
+ wakeup_pending = true;
+ spin_unlock_irqrestore(&xmm_lock, flags);
+ pr_info("%s: L2 race condition-CP wakeup"
+ " pending\n", __func__);
+ }
+ }
pr_info("L3\n");
if (wake_lock_active(&wakelock)) {
pr_info("%s: releasing wakelock before L3\n",
@@ -384,6 +420,12 @@ irqreturn_t baseband_xmm_power_ipc_ap_wake_irq(int irq, void *dev_id)
CP_initiated_L2toL0 = true;
baseband_xmm_set_power_status
(BBXMM_PS_L2TOL0);
+ } else if (baseband_xmm_powerstate ==
+ BBXMM_PS_L3) {
+ spin_lock(&xmm_lock);
+ wakeup_pending = true;
+ spin_unlock(&xmm_lock);
+ pr_info("CP L3 -> L0\n");
}
}
/* save gpio state */
@@ -500,6 +542,9 @@ static void baseband_xmm_power_L2_resume(void)
if (!baseband_power_driver_data)
return;
+
+ modem_sleep_flag = false;
+
if (CP_initiated_L2toL0) {
pr_info("CP L2->L0\n");
CP_initiated_L2toL0 = false;
@@ -702,6 +747,7 @@ static int baseband_xmm_power_driver_probe(struct platform_device *device)
= (struct baseband_power_platform_data *)
device->dev.platform_data;
struct device *dev = &device->dev;
+ unsigned long flags;
int err;
pr_debug("%s\n", __func__);
@@ -730,6 +776,8 @@ static int baseband_xmm_power_driver_probe(struct platform_device *device)
/* init wake lock */
wake_lock_init(&wakelock, WAKE_LOCK_SUSPEND, "baseband_xmm_power");
+ /* init spin lock */
+ spin_lock_init(&xmm_lock);
/* request baseband gpio(s) */
tegra_baseband_gpios[0].gpio = baseband_power_driver_data
->modem.xmm.bb_rst;
@@ -799,8 +847,11 @@ static int baseband_xmm_power_driver_probe(struct platform_device *device)
/* init state variables */
register_hsic_device = true;
- baseband_xmm_powerstate = BBXMM_PS_UNINIT;
CP_initiated_L2toL0 = false;
+ spin_lock_irqsave(&xmm_lock, flags);
+ baseband_xmm_powerstate = BBXMM_PS_UNINIT;
+ wakeup_pending = false;
+ spin_unlock_irqrestore(&xmm_lock, flags);
usb_register_notify(&usb_xmm_nb);
@@ -852,26 +903,15 @@ static int baseband_xmm_power_driver_remove(struct platform_device *device)
return 0;
}
-#ifdef CONFIG_PM
-static int baseband_xmm_power_driver_suspend(struct platform_device *device,
- pm_message_t state)
-{
- pr_debug("%s\n", __func__);
- return 0;
-}
-
-static int baseband_xmm_power_driver_resume(struct platform_device *device)
+static int baseband_xmm_power_driver_handle_resume(
+ struct baseband_power_platform_data *data)
{
- struct baseband_power_platform_data *data
- = (struct baseband_power_platform_data *)
- device->dev.platform_data;
int value;
int delay = 10000; /* maxmum delay in msec */
+ unsigned long flags;
pr_debug("%s\n", __func__);
-
- /* check for platform data */
- if (!baseband_power_driver_data)
+ if (!data)
return 0;
/* check if modem is on */
@@ -880,6 +920,8 @@ static int baseband_xmm_power_driver_resume(struct platform_device *device)
return 0;
}
+ modem_sleep_flag = false;
+
/* L3->L0 */
baseband_xmm_set_power_status(BBXMM_PS_L3TOL0);
value = gpio_get_value(data->modem.xmm.ipc_ap_wake);
@@ -898,27 +940,80 @@ static int baseband_xmm_power_driver_resume(struct platform_device *device)
pr_debug("gpio host wakeup low <-\n");
} else {
pr_info("CP L3 -> L0\n");
+ spin_lock_irqsave(&xmm_lock, flags);
+ /* Clear wakeup pending flag */
+ wakeup_pending = false;
+ spin_unlock_irqrestore(&xmm_lock, flags);
}
reenable_autosuspend = true;
return 0;
+
}
-#endif
-static struct platform_driver baseband_power_driver = {
- .probe = baseband_xmm_power_driver_probe,
- .remove = baseband_xmm_power_driver_remove,
#ifdef CONFIG_PM
+static int baseband_xmm_power_driver_suspend(struct device *dev)
+{
+ pr_debug("%s\n", __func__);
+ return 0;
+}
+
+static int baseband_xmm_power_driver_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct baseband_power_platform_data *data
+ = (struct baseband_power_platform_data *)
+ pdev->dev.platform_data;
+
+ pr_debug("%s\n", __func__);
+ baseband_xmm_power_driver_handle_resume(data);
+
+ return 0;
+}
+
+static int baseband_xmm_power_suspend_noirq(struct device *dev)
+{
+ unsigned long flags;
+
+ pr_debug("%s\n", __func__);
+ spin_lock_irqsave(&xmm_lock, flags);
+ if (wakeup_pending) {
+ pr_info("%s:**Abort Suspend: reason CP WAKEUP**\n", __func__);
+ wakeup_pending = false;
+ return -EBUSY;
+ }
+ spin_unlock_irqrestore(&xmm_lock, flags);
+ return 0;
+}
+
+static int baseband_xmm_power_resume_noirq(struct device *dev)
+{
+ pr_debug("%s\n", __func__);
+ return 0;
+}
+
+static const struct dev_pm_ops baseband_xmm_power_dev_pm_ops = {
+ .suspend_noirq = baseband_xmm_power_suspend_noirq,
+ .resume_noirq = baseband_xmm_power_resume_noirq,
.suspend = baseband_xmm_power_driver_suspend,
.resume = baseband_xmm_power_driver_resume,
+};
#endif
+
+static struct platform_driver baseband_power_driver = {
+ .probe = baseband_xmm_power_driver_probe,
+ .remove = baseband_xmm_power_driver_remove,
.driver = {
.name = "baseband_xmm_power",
+#ifdef CONFIG_PM
+ .pm = &baseband_xmm_power_dev_pm_ops,
+#endif
},
};
static int __init baseband_xmm_power_init(void)
{
+ pr_debug("%s\n", __func__);
return platform_driver_register(&baseband_power_driver);
}