[PATCH v3] Input: tsc2007 - enable GPIO chips that can sleep

Benjamin Bara posted 1 patch 1 year ago
drivers/input/touchscreen/tsc2007.h      |  1 +
drivers/input/touchscreen/tsc2007_core.c | 34 +++++++++++++++++++++++++++-----
2 files changed, 30 insertions(+), 5 deletions(-)
[PATCH v3] Input: tsc2007 - enable GPIO chips that can sleep
Posted by Benjamin Bara 1 year ago
From: Benjamin Bara <benjamin.bara@skidata.com>

This enables the usage of "can_sleep" GPIO chips as "pin up" GPIO.
This might be the case if the GPIO chip is an expander behind i2c.

As the pendown value is read during a hard IRQ, the read process is not
allowed to sleep. Therefore, find out if the read process can sleep and
if so, just return true. During the following soft IRQ, the read process
is allowed to sleep and therefore the actual value can be read.

Signed-off-by: Benjamin Bara <benjamin.bara@skidata.com>
Signed-off-by: Richard Leitner <richard.leitner@skidata.com>
---
This is basically a resend of v2 with extended commit message.

v2: https://lore.kernel.org/lkml/20220715074534.3116678-1-bbara93@gmail.com/

----
v3:
- extend commit message

v2:
- fix style mentioned by Christophe
---
 drivers/input/touchscreen/tsc2007.h      |  1 +
 drivers/input/touchscreen/tsc2007_core.c | 34 +++++++++++++++++++++++++++-----
 2 files changed, 30 insertions(+), 5 deletions(-)

diff --git a/drivers/input/touchscreen/tsc2007.h b/drivers/input/touchscreen/tsc2007.h
index 69b08dd6c8df..cdd90d727160 100644
--- a/drivers/input/touchscreen/tsc2007.h
+++ b/drivers/input/touchscreen/tsc2007.h
@@ -78,6 +78,7 @@ struct tsc2007 {
 	bool			stopped;
 
 	int			(*get_pendown_state)(struct device *);
+	int			(*get_pendown_state_cansleep)(struct device *dev);
 	void			(*clear_penirq)(void);
 
 	struct mutex		mlock;
diff --git a/drivers/input/touchscreen/tsc2007_core.c b/drivers/input/touchscreen/tsc2007_core.c
index 3c793fb70a0e..764cec1036e1 100644
--- a/drivers/input/touchscreen/tsc2007_core.c
+++ b/drivers/input/touchscreen/tsc2007_core.c
@@ -20,6 +20,7 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
 #include <linux/input.h>
 #include <linux/interrupt.h>
 #include <linux/i2c.h>
@@ -108,6 +109,14 @@ bool tsc2007_is_pen_down(struct tsc2007 *ts)
 	return ts->get_pendown_state(&ts->client->dev);
 }
 
+static bool tsc2007_is_pen_down_cansleep(struct tsc2007 *ts)
+{
+	if (!ts->get_pendown_state_cansleep)
+		return true;
+
+	return ts->get_pendown_state_cansleep(&ts->client->dev);
+}
+
 static irqreturn_t tsc2007_soft_irq(int irq, void *handle)
 {
 	struct tsc2007 *ts = handle;
@@ -115,7 +124,7 @@ static irqreturn_t tsc2007_soft_irq(int irq, void *handle)
 	struct ts_event tc;
 	u32 rt;
 
-	while (!ts->stopped && tsc2007_is_pen_down(ts)) {
+	while (!ts->stopped && tsc2007_is_pen_down_cansleep(ts)) {
 
 		/* pen is down, continue with the measurement */
 
@@ -125,7 +134,7 @@ static irqreturn_t tsc2007_soft_irq(int irq, void *handle)
 
 		rt = tsc2007_calculate_resistance(ts, &tc);
 
-		if (!rt && !ts->get_pendown_state) {
+		if (!rt && !ts->get_pendown_state_cansleep) {
 			/*
 			 * If pressure reported is 0 and we don't have
 			 * callback to check pendown state, we have to
@@ -229,6 +238,14 @@ static int tsc2007_get_pendown_state_gpio(struct device *dev)
 	return gpiod_get_value(ts->gpiod);
 }
 
+static int tsc2007_get_pendown_state_gpio_cansleep(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct tsc2007 *ts = i2c_get_clientdata(client);
+
+	return gpiod_get_value_cansleep(ts->gpiod);
+}
+
 static int tsc2007_probe_properties(struct device *dev, struct tsc2007 *ts)
 {
 	u32 val32;
@@ -264,10 +281,17 @@ static int tsc2007_probe_properties(struct device *dev, struct tsc2007 *ts)
 	if (IS_ERR(ts->gpiod))
 		return PTR_ERR(ts->gpiod);
 
-	if (ts->gpiod)
-		ts->get_pendown_state = tsc2007_get_pendown_state_gpio;
-	else
+	if (ts->gpiod) {
+		ts->get_pendown_state_cansleep = tsc2007_get_pendown_state_gpio_cansleep;
+
+		/* pendown pin is read during hard irq -> gpio chip is not allowed to sleep */
+		if (gpiod_to_chip(ts->gpiod) && !gpiod_to_chip(ts->gpiod)->can_sleep)
+			ts->get_pendown_state = tsc2007_get_pendown_state_gpio;
+		else
+			dev_dbg(dev, "Pen down GPIO chip can sleep\n");
+	} else {
 		dev_warn(dev, "Pen down GPIO is not specified in properties\n");
+	}
 
 	return 0;
 }

---
base-commit: 197b6b60ae7bc51dd0814953c562833143b292aa
change-id: 20230328-tsc2007-sleep-f65953ae32d0

Best regards,
-- 
Benjamin Bara <benjamin.bara@skidata.com>