diff options
author | Bo Kim <bok@nvidia.com> | 2013-09-13 16:48:03 +0900 |
---|---|---|
committer | Gabby Lee <galee@nvidia.com> | 2013-09-16 01:00:48 -0700 |
commit | 62c978972b14446f49433e08552cd14972369013 (patch) | |
tree | d252003ce0307bf88178ba32285a600cfc6cbc6e /drivers | |
parent | 0298d60e90c617b9e2b130e6ce57b478cc5052d1 (diff) |
input: touch: maxim_sti: Check thread stop condition
Problems:
If shutdown or remove is called when suspended, it could be stuck in
infinite loop.
Changes:
1. Check kthread_should_stop() in several loops should be helpful to
avoid infinite loop problem.
2. Add __pm_stay_awake() in resume() and __pm_relax() in suspend()
should be helpful to avoid messaging error touch fusion driver and touch
fusion daemon by user space freezing between suspend and resume.
3. shutdown()/remove() stops processing_thread() before killing daemon.
Bug 1366179
Change-Id: I68464164440418a53bd37ce1821e04b630c12aec
Signed-off-by: Bo Kim <bok@nvidia.com>
Reviewed-on: http://git-master/r/274196
Reviewed-by: Gabby Lee <galee@nvidia.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/input/touchscreen/maxim_sti.c | 64 |
1 files changed, 53 insertions, 11 deletions
diff --git a/drivers/input/touchscreen/maxim_sti.c b/drivers/input/touchscreen/maxim_sti.c index 0eaf41333b76..478ebe1b3d63 100644 --- a/drivers/input/touchscreen/maxim_sti.c +++ b/drivers/input/touchscreen/maxim_sti.c @@ -28,6 +28,10 @@ #include <linux/maxim_sti.h> #include <asm/byteorder.h> /* MUST include this header to get byte order */ +#ifdef CONFIG_PM_WAKELOCKS +#include <linux/pm_wakeup.h> +#endif + #define CREATE_TRACE_POINTS #include <trace/events/touchscreen_maxim.h> @@ -101,6 +105,9 @@ struct dev_data { #if NV_ENABLE_CPU_BOOST unsigned long last_irq_jiffies; #endif +#ifdef CONFIG_PM_WAKELOCKS + struct wakeup_source ws; +#endif }; static struct list_head dev_list; @@ -685,7 +692,7 @@ static void start_scan_canned(struct dev_data *dd) static int regulator_control(struct dev_data *dd, bool on) { - int ret; + int ret = 0; if (!dd->reg_avdd || !dd->reg_dvdd) return 0; @@ -705,13 +712,15 @@ static int regulator_control(struct dev_data *dd, bool on) return ret; } } else { - ret = regulator_disable(dd->reg_avdd); + if (regulator_is_enabled(dd->reg_avdd)) + ret = regulator_disable(dd->reg_avdd); if (ret < 0) { ERROR("Failed to disable regulator avdd: %d", ret); return ret; } - ret = regulator_disable(dd->reg_dvdd); + if (regulator_is_enabled(dd->reg_dvdd)) + ret = regulator_disable(dd->reg_dvdd); if (ret < 0) { ERROR("Failed to disable regulator dvdd: %d", ret); regulator_enable(dd->reg_avdd); @@ -751,6 +760,8 @@ static int suspend(struct device *dev) struct maxim_sti_pdata *pdata = dev->platform_data; int ret; + INFO("suspending..."); + if (dd->suspend_in_progress) return 0; @@ -767,6 +778,11 @@ static int suspend(struct device *dev) return ret; #endif +#ifdef CONFIG_PM_WAKELOCKS + __pm_relax(&dd->ws); +#endif + INFO("suspend...done"); + return 0; } @@ -776,16 +792,21 @@ static int resume(struct device *dev) struct maxim_sti_pdata *pdata = dev->platform_data; int ret; + INFO("resuming..."); + if (!dd->suspend_in_progress) return 0; +#ifdef CONFIG_PM_WAKELOCKS + __pm_stay_awake(&dd->ws); +#endif + #if SUSPEND_POWER_OFF /* power-up and reset-high */ pdata->reset(pdata, 0); ret = regulator_control(dd, true); if (ret < 0) return ret; - usleep_range(300, 400); pdata->reset(pdata, 1); #endif @@ -793,6 +814,9 @@ static int resume(struct device *dev) dd->resume_in_progress = true; wake_up_process(dd->thread); wait_for_completion(&dd->suspend_resume); + + INFO("resume...done"); + return 0; } @@ -1594,12 +1618,21 @@ static int processing_thread(void *arg) disable_irq(dd->spi->irq); stop_scan_canned(dd); complete(&dd->suspend_resume); + + INFO("%s: suspended.", __func__); + dd->input_ignore = true; - while (!dd->resume_in_progress) { + while (!dd->resume_in_progress && + !kthread_should_stop()) { /* the line below is a MUST */ set_current_state(TASK_INTERRUPTIBLE); schedule(); } + if (kthread_should_stop()) + break; + + INFO("%s: resuming.", __func__); + #if !SUSPEND_POWER_OFF start_scan_canned(dd); #endif @@ -1633,7 +1666,11 @@ static int processing_thread(void *arg) if (ret2 < 0) ERROR("could not allocate outgoing " \ "skb (%d)", ret2); - } while (ret != 0); + } while (ret != 0 && !kthread_should_stop()); + if (kthread_should_stop()) + break; + if (ret == 0) + INFO("%s: resumed.", __func__); } /* priority 4: service interrupt */ @@ -1777,6 +1814,10 @@ static int probe(struct spi_device *spi) dd->last_irq_jiffies = jiffies; #endif +#ifdef CONFIG_PM_WAKELOCKS + wakeup_source_init(&dd->ws, "touch_fusion"); + __pm_stay_awake(&dd->ws); +#endif /* start up Touch Fusion */ dd->start_fusion = true; wake_up_process(dd->thread); @@ -1807,6 +1848,9 @@ static int remove(struct spi_device *spi) if (dd->irq_registered) disable_irq(dd->spi->irq); + dd->nl_enabled = false; + (void)kthread_stop(dd->thread); + if (dd->fusion_process != (pid_t)0) (void)kill_pid(find_get_pid(dd->fusion_process), SIGKILL, 1); @@ -1819,8 +1863,6 @@ static int remove(struct spi_device *spi) /* 4) above step (3) insures that all Netlink senders are */ /* definitely gone and it is safe to free up outgoing skb buffer */ /* and incoming skb queue */ - dd->nl_enabled = false; - (void)kthread_stop(dd->thread); genl_unregister_family(&dd->nl_family); kfree_skb(dd->outgoing_skb); skb_queue_purge(&dd->incoming_skb_queue); @@ -1859,12 +1901,12 @@ static void shutdown(struct spi_device *spi) if (dd->irq_registered) disable_irq(dd->spi->irq); - if (dd->fusion_process != (pid_t)0) - (void)kill_pid(find_get_pid(dd->fusion_process), SIGKILL, 1); - dd->nl_enabled = false; (void)kthread_stop(dd->thread); + if (dd->fusion_process != (pid_t)0) + (void)kill_pid(find_get_pid(dd->fusion_process), SIGKILL, 1); + stop_scan_canned(dd); pdata->reset(pdata, 0); |