Commit 141586bc authored by Dmitry Torokhov's avatar Dmitry Torokhov

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: default avatarRichard Röjfors <richard.rojfors.ext@mocean-labs.com>
Signed-off-by: default avatarDmitry Torokhov <dtor@mail.ru>
parent 75fba3b0
...@@ -27,7 +27,8 @@ ...@@ -27,7 +27,8 @@
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/i2c/tsc2007.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_TEMP0 (0x0 << 4)
#define TSC2007_MEASURE_AUX (0x2 << 4) #define TSC2007_MEASURE_AUX (0x2 << 4)
...@@ -76,7 +77,7 @@ struct tsc2007 { ...@@ -76,7 +77,7 @@ struct tsc2007 {
u16 model; u16 model;
u16 x_plate_ohms; u16 x_plate_ohms;
unsigned pendown; bool pendown;
int irq; int irq;
int (*get_pendown_state)(void); int (*get_pendown_state)(void);
...@@ -131,18 +132,18 @@ static void tsc2007_send_event(void *tsc) ...@@ -131,18 +132,18 @@ static void tsc2007_send_event(void *tsc)
} else } else
rt = 0; 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 * the maximum. Don't report it to user space, repeat at least
* once more the measurement * once more the measurement
*/ */
if (rt > MAX_12BIT) { if (rt > MAX_12BIT) {
dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt); dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
schedule_delayed_work(&ts->work, TS_POLL_PERIOD);
return; 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 * state, even this controller has a pressure sensor. The pressure
* value can fluctuate for quite a while after lifting the pen and * value can fluctuate for quite a while after lifting the pen and
* in some cases may not even settle at the expected value. * in some cases may not even settle at the expected value.
...@@ -157,7 +158,7 @@ static void tsc2007_send_event(void *tsc) ...@@ -157,7 +158,7 @@ static void tsc2007_send_event(void *tsc)
dev_dbg(&ts->client->dev, "DOWN\n"); dev_dbg(&ts->client->dev, "DOWN\n");
input_report_key(input, BTN_TOUCH, 1); input_report_key(input, BTN_TOUCH, 1);
ts->pendown = 1; ts->pendown = true;
} }
input_report_abs(input, ABS_X, x); input_report_abs(input, ABS_X, x);
...@@ -169,8 +170,6 @@ static void tsc2007_send_event(void *tsc) ...@@ -169,8 +170,6 @@ static void tsc2007_send_event(void *tsc)
dev_dbg(&ts->client->dev, "point(%4d,%4d), pressure (%4u)\n", dev_dbg(&ts->client->dev, "point(%4d,%4d), pressure (%4u)\n",
x, y, rt); x, y, rt);
} }
schedule_delayed_work(&ts->work, TS_POLL_PERIOD);
} }
static int tsc2007_read_values(struct tsc2007 *tsc) static int tsc2007_read_values(struct tsc2007 *tsc)
...@@ -195,6 +194,7 @@ static void tsc2007_work(struct work_struct *work) ...@@ -195,6 +194,7 @@ static void tsc2007_work(struct work_struct *work)
{ {
struct tsc2007 *ts = struct tsc2007 *ts =
container_of(to_delayed_work(work), struct tsc2007, work); container_of(to_delayed_work(work), struct tsc2007, work);
if (unlikely(!ts->get_pendown_state() && ts->pendown)) { if (unlikely(!ts->get_pendown_state() && ts->pendown)) {
struct input_dev *input = ts->input; struct input_dev *input = ts->input;
...@@ -204,7 +204,7 @@ static void tsc2007_work(struct work_struct *work) ...@@ -204,7 +204,7 @@ static void tsc2007_work(struct work_struct *work)
input_report_abs(input, ABS_PRESSURE, 0); input_report_abs(input, ABS_PRESSURE, 0);
input_sync(input); input_sync(input);
ts->pendown = 0; ts->pendown = false;
enable_irq(ts->irq); enable_irq(ts->irq);
} else { } else {
/* pen is still down, continue with the measurement */ /* pen is still down, continue with the measurement */
...@@ -212,6 +212,9 @@ static void tsc2007_work(struct work_struct *work) ...@@ -212,6 +212,9 @@ static void tsc2007_work(struct work_struct *work)
tsc2007_read_values(ts); tsc2007_read_values(ts);
tsc2007_send_event(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) ...@@ -221,7 +224,8 @@ static irqreturn_t tsc2007_irq(int irq, void *handle)
if (likely(ts->get_pendown_state())) { if (likely(ts->get_pendown_state())) {
disable_irq_nosync(ts->irq); 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) if (ts->clear_penirq)
...@@ -230,8 +234,21 @@ static irqreturn_t tsc2007_irq(int irq, void *handle) ...@@ -230,8 +234,21 @@ static irqreturn_t tsc2007_irq(int irq, void *handle)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static int tsc2007_probe(struct i2c_client *client, static void tsc2007_free_irq(struct tsc2007 *ts)
const struct i2c_device_id *id) {
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 *ts;
struct tsc2007_platform_data *pdata = pdata = client->dev.platform_data; struct tsc2007_platform_data *pdata = pdata = client->dev.platform_data;
...@@ -255,17 +272,15 @@ static int tsc2007_probe(struct i2c_client *client, ...@@ -255,17 +272,15 @@ static int tsc2007_probe(struct i2c_client *client,
} }
ts->client = client; ts->client = client;
i2c_set_clientdata(client, ts); ts->irq = client->irq;
ts->input = input_dev; ts->input = input_dev;
INIT_DELAYED_WORK(&ts->work, tsc2007_work);
ts->model = pdata->model; ts->model = pdata->model;
ts->x_plate_ohms = pdata->x_plate_ohms; ts->x_plate_ohms = pdata->x_plate_ohms;
ts->get_pendown_state = pdata->get_pendown_state; ts->get_pendown_state = pdata->get_pendown_state;
ts->clear_penirq = pdata->clear_penirq; ts->clear_penirq = pdata->clear_penirq;
pdata->init_platform_hw();
snprintf(ts->phys, sizeof(ts->phys), snprintf(ts->phys, sizeof(ts->phys),
"%s/input0", dev_name(&client->dev)); "%s/input0", dev_name(&client->dev));
...@@ -280,11 +295,7 @@ static int tsc2007_probe(struct i2c_client *client, ...@@ -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_Y, 0, MAX_12BIT, 0, 0);
input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0); input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0);
tsc2007_read_values(ts); pdata->init_platform_hw();
ts->irq = client->irq;
INIT_DELAYED_WORK(&ts->work, tsc2007_work);
err = request_irq(ts->irq, tsc2007_irq, 0, err = request_irq(ts->irq, tsc2007_irq, 0,
client->dev.driver->name, ts); client->dev.driver->name, ts);
...@@ -297,29 +308,30 @@ static int tsc2007_probe(struct i2c_client *client, ...@@ -297,29 +308,30 @@ static int tsc2007_probe(struct i2c_client *client,
if (err) if (err)
goto err_free_irq; 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; return 0;
err_free_irq: err_free_irq:
free_irq(ts->irq, ts); tsc2007_free_irq(ts);
pdata->exit_platform_hw();
err_free_mem: err_free_mem:
input_free_device(input_dev); input_free_device(input_dev);
kfree(ts); kfree(ts);
return err; 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 *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(); pdata->exit_platform_hw();
free_irq(ts->irq, ts);
input_unregister_device(ts->input); input_unregister_device(ts->input);
kfree(ts); kfree(ts);
...@@ -340,7 +352,7 @@ static struct i2c_driver tsc2007_driver = { ...@@ -340,7 +352,7 @@ static struct i2c_driver tsc2007_driver = {
}, },
.id_table = tsc2007_idtable, .id_table = tsc2007_idtable,
.probe = tsc2007_probe, .probe = tsc2007_probe,
.remove = tsc2007_remove, .remove = __devexit_p(tsc2007_remove),
}; };
static int __init tsc2007_init(void) static int __init tsc2007_init(void)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment