Skip to content

Commit

Permalink
Input: ads7846 - handle IRQs that were latched during disabled IRQs
Browse files Browse the repository at this point in the history
The pen down IRQ will toggle during each X,Y,Z measurement cycle.
Even though the IRQ is disabled it will be latched and delivered
when after enable_irq. Thus in the IRQ handler we must avoid
starting a new measurement cycle when such an "unwanted" IRQ happens.
Add a get_pendown_state platform function, which will probably
determine this by reading the current GPIO level of the pen IRQ pin.

Move the IRQ reenabling from the SPI RX function to the timer. After
the last power down message the pen IRQ pin is still active for a
while and get_pendown_state would report incorrectly a pen down state.

When suspending we should check the ts->pending flag instead of
ts->pendown, since the timer can be pending regardless of ts->pendown.
Also if ts->pending is set we can be sure that the timer is running,
so no need to rearm it. Similarly if ts->pending is not set we can
be sure that the IRQ is enabled (and the timer is not).

Signed-off-by: Imre Deak <imre.deak@nokia.com>
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
  • Loading branch information
Imre Deak authored and Dmitry Torokhov committed Apr 12, 2006
1 parent 7de90a8 commit c9e617a
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 34 deletions.
82 changes: 48 additions & 34 deletions drivers/input/touchscreen/ads7846.c
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ struct ads7846 {
// FIXME remove "irq_disabled"
unsigned irq_disabled:1; /* P: lock */
unsigned disabled:1;

int (*get_pendown_state)(void);
};

/* leave chip selected when we're done, for quicker re-select? */
Expand Down Expand Up @@ -175,6 +177,12 @@ struct ser_req {
static void ads7846_enable(struct ads7846 *ts);
static void ads7846_disable(struct ads7846 *ts);

static int device_suspended(struct device *dev)
{
struct ads7846 *ts = dev_get_drvdata(dev);
return dev->power.power_state.event != PM_EVENT_ON || ts->disabled;
}

static int ads7846_read12_ser(struct device *dev, unsigned command)
{
struct spi_device *spi = to_spi_device(dev);
Expand Down Expand Up @@ -227,8 +235,10 @@ static int ads7846_read12_ser(struct device *dev, unsigned command)
for (i = 0; i < 6; i++)
spi_message_add_tail(&req->xfer[i], &req->msg);

ts->irq_disabled = 1;
disable_irq(spi->irq);
status = spi_sync(spi, &req->msg);
ts->irq_disabled = 0;
enable_irq(spi->irq);

if (req->msg.status)
Expand Down Expand Up @@ -333,7 +343,7 @@ static void ads7846_rx(void *ads)
if (x == MAX_12BIT)
x = 0;

if (x && z1 && ts->spi->dev.power.power_state.event == PM_EVENT_ON) {
if (likely(x && z1 && !device_suspended(&ts->spi->dev))) {
/* compute touch pressure resistance using equation #2 */
Rt = z2;
Rt -= z1;
Expand Down Expand Up @@ -377,20 +387,10 @@ static void ads7846_rx(void *ads)
x, y, Rt, Rt ? "" : " UP");
#endif

/* don't retrigger while we're suspended */
spin_lock_irqsave(&ts->lock, flags);

ts->pendown = (Rt != 0);
ts->pending = 0;

if (ts->spi->dev.power.power_state.event == PM_EVENT_ON) {
if (ts->pendown)
mod_timer(&ts->timer, jiffies + TS_POLL_PERIOD);
else if (ts->irq_disabled) {
ts->irq_disabled = 0;
enable_irq(ts->spi->irq);
}
}
mod_timer(&ts->timer, jiffies + TS_POLL_PERIOD);

spin_unlock_irqrestore(&ts->lock, flags);
}
Expand Down Expand Up @@ -431,10 +431,25 @@ static void ads7846_timer(unsigned long handle)
struct ads7846 *ts = (void *)handle;
int status = 0;

ts->msg_idx = 0;
status = spi_async(ts->spi, &ts->msg[0]);
if (status)
dev_err(&ts->spi->dev, "spi_async --> %d\n", status);
spin_lock_irq(&ts->lock);

if (unlikely(ts->msg_idx && !ts->pendown)) {
/* measurment cycle ended */
if (!device_suspended(&ts->spi->dev)) {
ts->irq_disabled = 0;
enable_irq(ts->spi->irq);
}
ts->pending = 0;
ts->msg_idx = 0;
} else {
/* pen is still down, continue with the measurement */
ts->msg_idx = 0;
status = spi_async(ts->spi, &ts->msg[0]);
if (status)
dev_err(&ts->spi->dev, "spi_async --> %d\n", status);
}

spin_unlock_irq(&ts->lock);
}

static irqreturn_t ads7846_irq(int irq, void *handle, struct pt_regs *regs)
Expand All @@ -443,7 +458,7 @@ static irqreturn_t ads7846_irq(int irq, void *handle, struct pt_regs *regs)
unsigned long flags;

spin_lock_irqsave(&ts->lock, flags);
if (likely(!ts->irq_disabled && !ts->disabled)) {
if (likely(ts->get_pendown_state())) {
if (!ts->irq_disabled) {
/* REVISIT irq logic for many ARM chips has cloned a
* bug wherein disabling an irq in its handler won't
Expand All @@ -452,10 +467,7 @@ static irqreturn_t ads7846_irq(int irq, void *handle, struct pt_regs *regs)
* that state here.
*/
ts->irq_disabled = 1;

disable_irq(ts->spi->irq);
}
if (!ts->pending) {
ts->pending = 1;
mod_timer(&ts->timer, jiffies);
}
Expand All @@ -473,20 +485,17 @@ static void ads7846_disable(struct ads7846 *ts)
if (ts->disabled)
return;

ts->disabled = 1;

/* are we waiting for IRQ, or polling? */
if (!ts->pendown) {
if (!ts->irq_disabled) {
ts->irq_disabled = 1;
disable_irq(ts->spi->irq);
}
if (!ts->pending) {
ts->irq_disabled = 1;
disable_irq(ts->spi->irq);
} else {
/* polling; force a final SPI completion;
* that will clean things up neatly
/* the timer will run at least once more, and
* leave everything in a clean state, IRQ disabled
*/
if (!ts->pending)
mod_timer(&ts->timer, jiffies);

while (ts->pendown || ts->pending) {
while (ts->pending) {
spin_unlock_irq(&ts->lock);
msleep(1);
spin_lock_irq(&ts->lock);
Expand All @@ -497,7 +506,6 @@ static void ads7846_disable(struct ads7846 *ts)
* leave it that way after every request
*/

ts->disabled = 1;
}

/* Must be called with ts->lock held */
Expand Down Expand Up @@ -566,6 +574,11 @@ static int __devinit ads7846_probe(struct spi_device *spi)
return -EINVAL;
}

if (pdata->get_pendown_state == NULL) {
dev_dbg(&spi->dev, "no get_pendown_state function?\n");
return -EINVAL;
}

/* We'd set the wordsize to 12 bits ... except that some controllers
* will then treat the 8 bit command words as 12 bits (and drop the
* four MSBs of the 12 bit result). Result: inputs must be shifted
Expand Down Expand Up @@ -596,6 +609,7 @@ static int __devinit ads7846_probe(struct spi_device *spi)
ts->x_plate_ohms = pdata->x_plate_ohms ? : 400;
ts->debounce_max = pdata->debounce_max ? : 1;
ts->debounce_tol = pdata->debounce_tol ? : 10;
ts->get_pendown_state = pdata->get_pendown_state;

snprintf(ts->phys, sizeof(ts->phys), "%s/input0", spi->dev.bus_id);

Expand Down Expand Up @@ -786,8 +800,8 @@ static int __devexit ads7846_remove(struct spi_device *spi)
device_remove_file(&spi->dev, &dev_attr_vaux);

free_irq(ts->spi->irq, ts);
if (ts->irq_disabled)
enable_irq(ts->spi->irq);
/* suspend left the IRQ disabled */
enable_irq(ts->spi->irq);

kfree(ts);

Expand Down
2 changes: 2 additions & 0 deletions include/linux/spi/ads7846.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,7 @@ struct ads7846_platform_data {

u16 debounce_max; /* max number of readings per sample */
u16 debounce_tol; /* tolerance used for filtering */

int (*get_pendown_state)(void);
};

0 comments on commit c9e617a

Please sign in to comment.