summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2009-07-24 23:14:16 -0700
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2009-07-26 11:20:57 -0700
commit141586bc57f6083f36c18d86e1cfa5916a1e7c05 (patch)
tree2458a98edd2cbddcd981cb21723127feb5428d00
parent75fba3b05d6ed82b975c1f8df8f8e08d5d81dee5 (diff)
Input: tsc2007 - properly shut off interrupts/delayed work
Properly shut off interrupts/delayed work by free-ing IRQ first and then ensuring that enable/disable is balanced. Also add __devinit/__devexit markings, restore poll delay/period scheduling logic, make sure we call exit_platform_hw() method when probe fails. Tested-by: Richard Röjfors <richard.rojfors.ext@mocean-labs.com> Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
-rw-r--r--drivers/input/touchscreen/tsc2007.c72
1 files changed, 42 insertions, 30 deletions
diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c
index b512697b227d..75fbd753a4d2 100644
--- a/drivers/input/touchscreen/tsc2007.c
+++ b/drivers/input/touchscreen/tsc2007.c
@@ -27,7 +27,8 @@
#include <linux/i2c.h>
#include <linux/i2c/tsc2007.h>
-#define TS_POLL_PERIOD msecs_to_jiffies(1) /* ms delay between samples */
+#define TS_POLL_DELAY 1 /* ms delay between samples */
+#define TS_POLL_PERIOD 1 /* ms delay between samples */
#define TSC2007_MEASURE_TEMP0 (0x0 << 4)
#define TSC2007_MEASURE_AUX (0x2 << 4)
@@ -76,7 +77,7 @@ struct tsc2007 {
u16 model;
u16 x_plate_ohms;
- unsigned pendown;
+ bool pendown;
int irq;
int (*get_pendown_state)(void);
@@ -131,18 +132,18 @@ static void tsc2007_send_event(void *tsc)
} else
rt = 0;
- /* Sample found inconsistent by debouncing or pressure is beyond
+ /*
+ * Sample found inconsistent by debouncing or pressure is beyond
* the maximum. Don't report it to user space, repeat at least
* once more the measurement
*/
if (rt > MAX_12BIT) {
dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
-
- schedule_delayed_work(&ts->work, TS_POLL_PERIOD);
return;
}
- /* NOTE: We can't rely on the pressure to determine the pen down
+ /*
+ * NOTE: We can't rely on the pressure to determine the pen down
* state, even this controller has a pressure sensor. The pressure
* value can fluctuate for quite a while after lifting the pen and
* in some cases may not even settle at the expected value.
@@ -157,7 +158,7 @@ static void tsc2007_send_event(void *tsc)
dev_dbg(&ts->client->dev, "DOWN\n");
input_report_key(input, BTN_TOUCH, 1);
- ts->pendown = 1;
+ ts->pendown = true;
}
input_report_abs(input, ABS_X, x);
@@ -169,8 +170,6 @@ static void tsc2007_send_event(void *tsc)
dev_dbg(&ts->client->dev, "point(%4d,%4d), pressure (%4u)\n",
x, y, rt);
}
-
- schedule_delayed_work(&ts->work, TS_POLL_PERIOD);
}
static int tsc2007_read_values(struct tsc2007 *tsc)
@@ -195,6 +194,7 @@ static void tsc2007_work(struct work_struct *work)
{
struct tsc2007 *ts =
container_of(to_delayed_work(work), struct tsc2007, work);
+
if (unlikely(!ts->get_pendown_state() && ts->pendown)) {
struct input_dev *input = ts->input;
@@ -204,7 +204,7 @@ static void tsc2007_work(struct work_struct *work)
input_report_abs(input, ABS_PRESSURE, 0);
input_sync(input);
- ts->pendown = 0;
+ ts->pendown = false;
enable_irq(ts->irq);
} else {
/* pen is still down, continue with the measurement */
@@ -212,6 +212,9 @@ static void tsc2007_work(struct work_struct *work)
tsc2007_read_values(ts);
tsc2007_send_event(ts);
+
+ schedule_delayed_work(&ts->work,
+ msecs_to_jiffies(TS_POLL_PERIOD));
}
}
@@ -221,7 +224,8 @@ static irqreturn_t tsc2007_irq(int irq, void *handle)
if (likely(ts->get_pendown_state())) {
disable_irq_nosync(ts->irq);
- schedule_delayed_work(&ts->work, 0);
+ schedule_delayed_work(&ts->work,
+ msecs_to_jiffies(TS_POLL_DELAY));
}
if (ts->clear_penirq)
@@ -230,8 +234,21 @@ static irqreturn_t tsc2007_irq(int irq, void *handle)
return IRQ_HANDLED;
}
-static int tsc2007_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static void tsc2007_free_irq(struct tsc2007 *ts)
+{
+ free_irq(ts->irq, ts);
+ if (cancel_delayed_work_sync(&ts->work)) {
+ /*
+ * Work was pending, therefore we need to enable
+ * IRQ here to balance the disable_irq() done in the
+ * interrupt handler.
+ */
+ enable_irq(ts->irq);
+ }
+}
+
+static int __devinit tsc2007_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
{
struct tsc2007 *ts;
struct tsc2007_platform_data *pdata = pdata = client->dev.platform_data;
@@ -255,17 +272,15 @@ static int tsc2007_probe(struct i2c_client *client,
}
ts->client = client;
- i2c_set_clientdata(client, ts);
-
+ ts->irq = client->irq;
ts->input = input_dev;
+ INIT_DELAYED_WORK(&ts->work, tsc2007_work);
ts->model = pdata->model;
ts->x_plate_ohms = pdata->x_plate_ohms;
ts->get_pendown_state = pdata->get_pendown_state;
ts->clear_penirq = pdata->clear_penirq;
- pdata->init_platform_hw();
-
snprintf(ts->phys, sizeof(ts->phys),
"%s/input0", dev_name(&client->dev));
@@ -280,11 +295,7 @@ static int tsc2007_probe(struct i2c_client *client,
input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0);
input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0);
- tsc2007_read_values(ts);
-
- ts->irq = client->irq;
-
- INIT_DELAYED_WORK(&ts->work, tsc2007_work);
+ pdata->init_platform_hw();
err = request_irq(ts->irq, tsc2007_irq, 0,
client->dev.driver->name, ts);
@@ -297,29 +308,30 @@ static int tsc2007_probe(struct i2c_client *client,
if (err)
goto err_free_irq;
- dev_info(&client->dev, "registered with irq (%d)\n", ts->irq);
+ i2c_set_clientdata(client, ts);
+
+ tsc2007_read_values(ts);
return 0;
err_free_irq:
- free_irq(ts->irq, ts);
+ tsc2007_free_irq(ts);
+ pdata->exit_platform_hw();
err_free_mem:
input_free_device(input_dev);
kfree(ts);
return err;
}
-static int tsc2007_remove(struct i2c_client *client)
+static int __devexit tsc2007_remove(struct i2c_client *client)
{
struct tsc2007 *ts = i2c_get_clientdata(client);
- struct tsc2007_platform_data *pdata;
+ struct tsc2007_platform_data *pdata = client->dev.platform_data;
- cancel_delayed_work_sync(&ts->work);
+ tsc2007_free_irq(ts);
- pdata = client->dev.platform_data;
pdata->exit_platform_hw();
- free_irq(ts->irq, ts);
input_unregister_device(ts->input);
kfree(ts);
@@ -340,7 +352,7 @@ static struct i2c_driver tsc2007_driver = {
},
.id_table = tsc2007_idtable,
.probe = tsc2007_probe,
- .remove = tsc2007_remove,
+ .remove = __devexit_p(tsc2007_remove),
};
static int __init tsc2007_init(void)