Skip to content

Commit

Permalink
Input: eeti_ts - read hardware state once after wakeup
Browse files Browse the repository at this point in the history
For systems in which the touch IRQ is acting as wakeup source, and that do
not support level-driven interrupts, the interrupt controller might not
latch the GPIO IRQ during sleep. In such cases, the interrupt will never
occur again after resume, hence the touch screen appears dead.

To fix this, check for the assertion of the attn gpio, and read form the
controller once in the resume path to read the hardware status and
to arm the IRQ again.

Introduce a mutex to guard eeti_ts_read() against parallel invocations
from different contexts.

Signed-off-by: Daniel Mack <[email protected]>
Reported-by: Sven Neumann <[email protected]>
Signed-off-by: Dmitry Torokhov <[email protected]>
  • Loading branch information
zonque authored and dtor committed May 15, 2019
1 parent 7f7a020 commit b283d0c
Showing 1 changed file with 56 additions and 15 deletions.
71 changes: 56 additions & 15 deletions drivers/input/touchscreen/eeti_ts.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ struct eeti_ts {
struct input_dev *input;
struct gpio_desc *attn_gpio;
struct touchscreen_properties props;
struct mutex mutex;
bool running;
};

Expand Down Expand Up @@ -75,42 +76,80 @@ static void eeti_ts_report_event(struct eeti_ts *eeti, u8 *buf)
input_sync(eeti->input);
}

static int eeti_ts_read(struct eeti_ts *eeti)
{
int len, error;
char buf[6];

len = i2c_master_recv(eeti->client, buf, sizeof(buf));
if (len != sizeof(buf)) {
error = len < 0 ? len : -EIO;
dev_err(&eeti->client->dev,
"failed to read touchscreen data: %d\n",
error);
return error;
}

/* Motion packet */
if (buf[0] & 0x80)
eeti_ts_report_event(eeti, buf);

return 0;
}

static irqreturn_t eeti_ts_isr(int irq, void *dev_id)
{
struct eeti_ts *eeti = dev_id;
int len;
int error;
char buf[6];

mutex_lock(&eeti->mutex);

do {
len = i2c_master_recv(eeti->client, buf, sizeof(buf));
if (len != sizeof(buf)) {
error = len < 0 ? len : -EIO;
dev_err(&eeti->client->dev,
"failed to read touchscreen data: %d\n",
error);
/*
* If we have attention GPIO, trust it. Otherwise we'll read
* once and exit. We assume that in this case we are using
* level triggered interrupt and it will get raised again
* if/when there is more data.
*/
if (eeti->attn_gpio &&
!gpiod_get_value_cansleep(eeti->attn_gpio)) {
break;
}

if (buf[0] & 0x80) {
/* Motion packet */
eeti_ts_report_event(eeti, buf);
}
} while (eeti->running &&
eeti->attn_gpio && gpiod_get_value_cansleep(eeti->attn_gpio));
error = eeti_ts_read(eeti);
if (error)
break;

} while (eeti->running && eeti->attn_gpio);

mutex_unlock(&eeti->mutex);
return IRQ_HANDLED;
}

static void eeti_ts_start(struct eeti_ts *eeti)
{
mutex_lock(&eeti->mutex);

eeti->running = true;
wmb();
enable_irq(eeti->client->irq);

/*
* Kick the controller in case we are using edge interrupt and
* we missed our edge while interrupt was disabled. We expect
* the attention GPIO to be wired in this case.
*/
if (eeti->attn_gpio && gpiod_get_value_cansleep(eeti->attn_gpio))
eeti_ts_read(eeti);

mutex_unlock(&eeti->mutex);
}

static void eeti_ts_stop(struct eeti_ts *eeti)
{
/*
* Not locking here, just setting a flag and expect that the
* interrupt thread will notice the flag eventually.
*/
eeti->running = false;
wmb();
disable_irq(eeti->client->irq);
Expand Down Expand Up @@ -153,6 +192,8 @@ static int eeti_ts_probe(struct i2c_client *client,
return -ENOMEM;
}

mutex_init(&eeti->mutex);

input = devm_input_allocate_device(dev);
if (!input) {
dev_err(dev, "Failed to allocate input device.\n");
Expand Down

0 comments on commit b283d0c

Please sign in to comment.