summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorBo Kim <bok@nvidia.com>2013-09-13 16:48:03 +0900
committerGabby Lee <galee@nvidia.com>2013-09-16 01:00:48 -0700
commit62c978972b14446f49433e08552cd14972369013 (patch)
treed252003ce0307bf88178ba32285a600cfc6cbc6e /drivers
parent0298d60e90c617b9e2b130e6ce57b478cc5052d1 (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.c64
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);