diff options
author | Seshendra Gadagottu <sgadagottu@nvidia.com> | 2012-01-27 15:23:18 +0530 |
---|---|---|
committer | Rohan Somvanshi <rsomvanshi@nvidia.com> | 2012-02-03 05:21:02 -0800 |
commit | 0d932c60e6cc9cd1b26b94f0d882fe1716c7dc28 (patch) | |
tree | 7cc45d8efb113aa33e6ebcee127a77858c8eaac0 /arch/arm/mach-tegra/baseband-xmm-power.c | |
parent | 59d63497a26dc94612e14c905db6c2368dcdbf43 (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
Diffstat (limited to 'arch/arm/mach-tegra/baseband-xmm-power.c')
-rw-r--r-- | arch/arm/mach-tegra/baseband-xmm-power.c | 137 |
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); } |