All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/9] Input: iqs5xx - more enhancements and optimizations
@ 2021-03-13 19:12 Jeff LaBundy
  2021-03-13 19:12 ` [PATCH v2 1/9] Input: iqs5xx - update vendor's URL Jeff LaBundy
                   ` (8 more replies)
  0 siblings, 9 replies; 19+ messages in thread
From: Jeff LaBundy @ 2021-03-13 19:12 UTC (permalink / raw)
  To: dmitry.torokhov, robh+dt; +Cc: linux-input, devicetree, Jeff LaBundy

This series continues recent work to further enhance and optimize the Azoteq
IQS550/572/525 trackpad/touchscreen controller driver. In addition to having
been made a bit smaller, the driver now supports some additional use-cases.

Patches 3 and 8 are based on [1] and [2], respectively. Also included in the
series is an updated binding, now presented in YAML.

[1] https://patchwork.kernel.org/patch/12028203/
[2] https://patchwork.kernel.org/patch/12028223/

Jeff LaBundy (9):
  Input: iqs5xx - update vendor's URL
  Input: iqs5xx - optimize axis definition and validation
  Input: iqs5xx - expose firmware revision to user space
  Input: iqs5xx - remove superfluous revision validation
  Input: iqs5xx - close bootloader using hardware reset
  Input: iqs5xx - prevent interrupt storm during removal
  Input: iqs5xx - suspend or resume regardless of users
  Input: iqs5xx - make reset GPIO optional
  dt-bindings: input: iqs5xx: Convert to YAML

 .../input/touchscreen/azoteq,iqs5xx.yaml      |  75 ++++++
 .../bindings/input/touchscreen/iqs5xx.txt     |  80 ------
 drivers/input/touchscreen/iqs5xx.c            | 238 +++++++-----------
 3 files changed, 164 insertions(+), 229 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/input/touchscreen/azoteq,iqs5xx.yaml
 delete mode 100644 Documentation/devicetree/bindings/input/touchscreen/iqs5xx.txt

--
2.17.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v2 1/9] Input: iqs5xx - update vendor's URL
  2021-03-13 19:12 [PATCH v2 0/9] Input: iqs5xx - more enhancements and optimizations Jeff LaBundy
@ 2021-03-13 19:12 ` Jeff LaBundy
  2021-03-22  4:00   ` Dmitry Torokhov
  2021-03-13 19:12 ` [PATCH v2 2/9] Input: iqs5xx - optimize axis definition and validation Jeff LaBundy
                   ` (7 subsequent siblings)
  8 siblings, 1 reply; 19+ messages in thread
From: Jeff LaBundy @ 2021-03-13 19:12 UTC (permalink / raw)
  To: dmitry.torokhov, robh+dt; +Cc: linux-input, devicetree, Jeff LaBundy

Replace 'http' with 'https' and correct the spelling of the nearby
word 'datasheet'.

Signed-off-by: Jeff LaBundy <jeff@labundy.com>
---
Changes in v2:
 - None

 drivers/input/touchscreen/iqs5xx.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/input/touchscreen/iqs5xx.c b/drivers/input/touchscreen/iqs5xx.c
index 54f30038dca4..403e251a5e7d 100644
--- a/drivers/input/touchscreen/iqs5xx.c
+++ b/drivers/input/touchscreen/iqs5xx.c
@@ -8,7 +8,7 @@
  * made available by the vendor. Firmware files may be pushed to the device's
  * nonvolatile memory by writing the filename to the 'fw_file' sysfs control.
  *
- * Link to PC-based configuration tool and data sheet: http://www.azoteq.com/
+ * Link to PC-based configuration tool and datasheet: https://www.azoteq.com/
  */

 #include <linux/bits.h>
--
2.17.1


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v2 2/9] Input: iqs5xx - optimize axis definition and validation
  2021-03-13 19:12 [PATCH v2 0/9] Input: iqs5xx - more enhancements and optimizations Jeff LaBundy
  2021-03-13 19:12 ` [PATCH v2 1/9] Input: iqs5xx - update vendor's URL Jeff LaBundy
@ 2021-03-13 19:12 ` Jeff LaBundy
  2021-03-22  4:02   ` Dmitry Torokhov
  2021-03-13 19:12 ` [PATCH v2 3/9] Input: iqs5xx - expose firmware revision to user space Jeff LaBundy
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 19+ messages in thread
From: Jeff LaBundy @ 2021-03-13 19:12 UTC (permalink / raw)
  To: dmitry.torokhov, robh+dt; +Cc: linux-input, devicetree, Jeff LaBundy

Set the maximum ABS_MT_PRESSURE value and use the existing U16_MAX
definition instead of a magic number to validate ABS_MT_POSITION_X
and ABS_MT_POSITION_Y.

Also use input_set_abs_params() rather than input_abs_set_max() to
avoid having to call input_set_capability() separately.

Signed-off-by: Jeff LaBundy <jeff@labundy.com>
---
Changes in v2:
 - None

 drivers/input/touchscreen/iqs5xx.c | 30 ++++++++++++++----------------
 1 file changed, 14 insertions(+), 16 deletions(-)

diff --git a/drivers/input/touchscreen/iqs5xx.c b/drivers/input/touchscreen/iqs5xx.c
index 403e251a5e7d..2a4e048f1400 100644
--- a/drivers/input/touchscreen/iqs5xx.c
+++ b/drivers/input/touchscreen/iqs5xx.c
@@ -32,7 +32,6 @@
 #define IQS5XX_NUM_RETRIES	10
 #define IQS5XX_NUM_CONTACTS	5
 #define IQS5XX_WR_BYTES_MAX	2
-#define IQS5XX_XY_RES_MAX	0xFFFE

 #define IQS5XX_PROD_NUM_IQS550	40
 #define IQS5XX_PROD_NUM_IQS572	58
@@ -504,10 +503,6 @@ static int iqs5xx_axis_init(struct i2c_client *client)
 		input->open = iqs5xx_open;
 		input->close = iqs5xx_close;

-		input_set_capability(input, EV_ABS, ABS_MT_POSITION_X);
-		input_set_capability(input, EV_ABS, ABS_MT_POSITION_Y);
-		input_set_capability(input, EV_ABS, ABS_MT_PRESSURE);
-
 		input_set_drvdata(input, iqs5xx);
 		iqs5xx->input = input;
 	}
@@ -520,26 +515,29 @@ static int iqs5xx_axis_init(struct i2c_client *client)
 	if (error)
 		return error;

-	input_abs_set_max(iqs5xx->input, ABS_MT_POSITION_X, max_x);
-	input_abs_set_max(iqs5xx->input, ABS_MT_POSITION_Y, max_y);
+	input_set_abs_params(iqs5xx->input, ABS_MT_POSITION_X, 0, max_x, 0, 0);
+	input_set_abs_params(iqs5xx->input, ABS_MT_POSITION_Y, 0, max_y, 0, 0);
+	input_set_abs_params(iqs5xx->input, ABS_MT_PRESSURE, 0, U16_MAX, 0, 0);

 	touchscreen_parse_properties(iqs5xx->input, true, prop);

-	if (prop->max_x > IQS5XX_XY_RES_MAX) {
-		dev_err(&client->dev, "Invalid maximum x-coordinate: %u > %u\n",
-			prop->max_x, IQS5XX_XY_RES_MAX);
+	/*
+	 * The device reserves 0xFFFF for coordinates that correspond to slots
+	 * which are not in a state of touch.
+	 */
+	if (prop->max_x >= U16_MAX || prop->max_y >= U16_MAX) {
+		dev_err(&client->dev, "Invalid touchscreen size: %u*%u\n",
+			prop->max_x, prop->max_y);
 		return -EINVAL;
-	} else if (prop->max_x != max_x) {
+	}
+
+	if (prop->max_x != max_x) {
 		error = iqs5xx_write_word(client, IQS5XX_X_RES, prop->max_x);
 		if (error)
 			return error;
 	}

-	if (prop->max_y > IQS5XX_XY_RES_MAX) {
-		dev_err(&client->dev, "Invalid maximum y-coordinate: %u > %u\n",
-			prop->max_y, IQS5XX_XY_RES_MAX);
-		return -EINVAL;
-	} else if (prop->max_y != max_y) {
+	if (prop->max_y != max_y) {
 		error = iqs5xx_write_word(client, IQS5XX_Y_RES, prop->max_y);
 		if (error)
 			return error;
--
2.17.1


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v2 3/9] Input: iqs5xx - expose firmware revision to user space
  2021-03-13 19:12 [PATCH v2 0/9] Input: iqs5xx - more enhancements and optimizations Jeff LaBundy
  2021-03-13 19:12 ` [PATCH v2 1/9] Input: iqs5xx - update vendor's URL Jeff LaBundy
  2021-03-13 19:12 ` [PATCH v2 2/9] Input: iqs5xx - optimize axis definition and validation Jeff LaBundy
@ 2021-03-13 19:12 ` Jeff LaBundy
  2021-03-22  4:02   ` Dmitry Torokhov
  2021-03-13 19:12 ` [PATCH v2 4/9] Input: iqs5xx - remove superfluous revision validation Jeff LaBundy
                   ` (5 subsequent siblings)
  8 siblings, 1 reply; 19+ messages in thread
From: Jeff LaBundy @ 2021-03-13 19:12 UTC (permalink / raw)
  To: dmitry.torokhov, robh+dt; +Cc: linux-input, devicetree, Jeff LaBundy

Add the read-only 'fw_info' attribute which reports information
about the device's firmware in the following format:

a.b.c.d:e.f

Where:

a = Product number (e.g. 40 for IQS550)
b = Project number (e.g. 15)
c = Firmware revision (major)
d = Firmware revision (minor)
e = Customer-assigned exported file version (major)
f = Customer-assigned exported file version (minor)

As part of the corresponding rework to uses of 'bl_status', the
IQS5XX_BL_STATUS_RESET definition is dropped with 0 used in its
place instead.

Signed-off-by: Jeff LaBundy <jeff@labundy.com>
---
Changes in v2:
 - None

 drivers/input/touchscreen/iqs5xx.c | 56 +++++++++++++++++++++---------
 1 file changed, 40 insertions(+), 16 deletions(-)

diff --git a/drivers/input/touchscreen/iqs5xx.c b/drivers/input/touchscreen/iqs5xx.c
index 2a4e048f1400..f36d170e14b2 100644
--- a/drivers/input/touchscreen/iqs5xx.c
+++ b/drivers/input/touchscreen/iqs5xx.c
@@ -63,6 +63,7 @@
 #define IQS5XX_SYS_CFG1		0x058F
 #define IQS5XX_X_RES		0x066E
 #define IQS5XX_Y_RES		0x0670
+#define IQS5XX_EXP_FILE		0x0677
 #define IQS5XX_CHKSM		0x83C0
 #define IQS5XX_APP		0x8400
 #define IQS5XX_CSTM		0xBE00
@@ -86,22 +87,12 @@
 #define IQS5XX_BL_CMD_CRC	0x03
 #define IQS5XX_BL_BLK_LEN_MAX	64
 #define IQS5XX_BL_ID		0x0200
-#define IQS5XX_BL_STATUS_RESET	0x00
 #define IQS5XX_BL_STATUS_AVAIL	0xA5
 #define IQS5XX_BL_STATUS_NONE	0xEE
 #define IQS5XX_BL_CRC_PASS	0x00
 #define IQS5XX_BL_CRC_FAIL	0x01
 #define IQS5XX_BL_ATTEMPTS	3

-struct iqs5xx_private {
-	struct i2c_client *client;
-	struct input_dev *input;
-	struct gpio_desc *reset_gpio;
-	struct touchscreen_properties prop;
-	struct mutex lock;
-	u8 bl_status;
-};
-
 struct iqs5xx_dev_id_info {
 	__be16 prod_num;
 	__be16 proj_num;
@@ -133,6 +124,16 @@ struct iqs5xx_status {
 	struct iqs5xx_touch_data touch_data[IQS5XX_NUM_CONTACTS];
 } __packed;

+struct iqs5xx_private {
+	struct i2c_client *client;
+	struct input_dev *input;
+	struct gpio_desc *reset_gpio;
+	struct touchscreen_properties prop;
+	struct mutex lock;
+	struct iqs5xx_dev_id_info dev_id_info;
+	u8 exp_file[2];
+};
+
 static int iqs5xx_read_burst(struct i2c_client *client,
 			     u16 reg, void *val, u16 len)
 {
@@ -445,7 +446,7 @@ static int iqs5xx_set_state(struct i2c_client *client, u8 state)
 	struct iqs5xx_private *iqs5xx = i2c_get_clientdata(client);
 	int error1, error2;

-	if (iqs5xx->bl_status == IQS5XX_BL_STATUS_RESET)
+	if (!iqs5xx->dev_id_info.bl_status)
 		return 0;

 	mutex_lock(&iqs5xx->lock);
@@ -615,6 +616,11 @@ static int iqs5xx_dev_init(struct i2c_client *client)
 		return -EINVAL;
 	}

+	error = iqs5xx_read_burst(client, IQS5XX_EXP_FILE,
+				  iqs5xx->exp_file, sizeof(iqs5xx->exp_file));
+	if (error)
+		return error;
+
 	error = iqs5xx_axis_init(client);
 	if (error)
 		return error;
@@ -638,7 +644,7 @@ static int iqs5xx_dev_init(struct i2c_client *client)
 	if (error)
 		return error;

-	iqs5xx->bl_status = dev_id_info->bl_status;
+	iqs5xx->dev_id_info = *dev_id_info;

 	/*
 	 * The following delay allows ATI to complete before the open and close
@@ -664,7 +670,7 @@ static irqreturn_t iqs5xx_irq(int irq, void *data)
 	 * RDY output during bootloader mode. If the device operates outside of
 	 * bootloader mode, the input device is guaranteed to be allocated.
 	 */
-	if (iqs5xx->bl_status == IQS5XX_BL_STATUS_RESET)
+	if (!iqs5xx->dev_id_info.bl_status)
 		return IRQ_NONE;

 	error = iqs5xx_read_burst(client, IQS5XX_SYS_INFO0,
@@ -853,7 +859,7 @@ static int iqs5xx_fw_file_write(struct i2c_client *client, const char *fw_file)
 	int error, error_bl = 0;
 	u8 *pmap;

-	if (iqs5xx->bl_status == IQS5XX_BL_STATUS_NONE)
+	if (iqs5xx->dev_id_info.bl_status == IQS5XX_BL_STATUS_NONE)
 		return -EPERM;

 	pmap = kzalloc(IQS5XX_PMAP_LEN, GFP_KERNEL);
@@ -873,7 +879,7 @@ static int iqs5xx_fw_file_write(struct i2c_client *client, const char *fw_file)
 	 */
 	disable_irq(client->irq);

-	iqs5xx->bl_status = IQS5XX_BL_STATUS_RESET;
+	iqs5xx->dev_id_info.bl_status = 0;

 	error = iqs5xx_bl_cmd(client, IQS5XX_BL_CMD_VER, 0);
 	if (error) {
@@ -906,7 +912,7 @@ static int iqs5xx_fw_file_write(struct i2c_client *client, const char *fw_file)

 	error_bl = error;
 	error = iqs5xx_dev_init(client);
-	if (!error && iqs5xx->bl_status == IQS5XX_BL_STATUS_RESET)
+	if (!error && !iqs5xx->dev_id_info.bl_status)
 		error = -EINVAL;

 	enable_irq(client->irq);
@@ -966,10 +972,28 @@ static ssize_t fw_file_store(struct device *dev,
 	return count;
 }

+static ssize_t fw_info_show(struct device *dev,
+			    struct device_attribute *attr, char *buf)
+{
+	struct iqs5xx_private *iqs5xx = dev_get_drvdata(dev);
+
+	if (!iqs5xx->dev_id_info.bl_status)
+		return -ENODATA;
+
+	return scnprintf(buf, PAGE_SIZE, "%u.%u.%u.%u:%u.%u\n",
+			 be16_to_cpu(iqs5xx->dev_id_info.prod_num),
+			 be16_to_cpu(iqs5xx->dev_id_info.proj_num),
+			 iqs5xx->dev_id_info.major_ver,
+			 iqs5xx->dev_id_info.minor_ver,
+			 iqs5xx->exp_file[0], iqs5xx->exp_file[1]);
+}
+
 static DEVICE_ATTR_WO(fw_file);
+static DEVICE_ATTR_RO(fw_info);

 static struct attribute *iqs5xx_attrs[] = {
 	&dev_attr_fw_file.attr,
+	&dev_attr_fw_info.attr,
 	NULL,
 };

--
2.17.1


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v2 4/9] Input: iqs5xx - remove superfluous revision validation
  2021-03-13 19:12 [PATCH v2 0/9] Input: iqs5xx - more enhancements and optimizations Jeff LaBundy
                   ` (2 preceding siblings ...)
  2021-03-13 19:12 ` [PATCH v2 3/9] Input: iqs5xx - expose firmware revision to user space Jeff LaBundy
@ 2021-03-13 19:12 ` Jeff LaBundy
  2021-03-22  4:03   ` Dmitry Torokhov
  2021-03-13 19:12 ` [PATCH v2 5/9] Input: iqs5xx - close bootloader using hardware reset Jeff LaBundy
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 19+ messages in thread
From: Jeff LaBundy @ 2021-03-13 19:12 UTC (permalink / raw)
  To: dmitry.torokhov, robh+dt; +Cc: linux-input, devicetree, Jeff LaBundy

The vendor-assigned firmware project number is restricted to the
generic project number (15); however the vendor may assign other
project numbers to specific applications and customers.

These custom project numbers may be based on forwards-compatible
firmware revision 1.x. However, the driver unnecessarily rejects
anything older than firmware revision 2.0.

To support other applications, remove these unnecessarily strict
checks and enter the bootloader only for truly incompatible A000
devices.

Signed-off-by: Jeff LaBundy <jeff@labundy.com>
---
Changes in v2:
 - None

 drivers/input/touchscreen/iqs5xx.c | 40 ++++++------------------------
 1 file changed, 8 insertions(+), 32 deletions(-)

diff --git a/drivers/input/touchscreen/iqs5xx.c b/drivers/input/touchscreen/iqs5xx.c
index f36d170e14b2..0920516124c7 100644
--- a/drivers/input/touchscreen/iqs5xx.c
+++ b/drivers/input/touchscreen/iqs5xx.c
@@ -36,9 +36,6 @@
 #define IQS5XX_PROD_NUM_IQS550	40
 #define IQS5XX_PROD_NUM_IQS572	58
 #define IQS5XX_PROD_NUM_IQS525	52
-#define IQS5XX_PROJ_NUM_A000	0
-#define IQS5XX_PROJ_NUM_B000	15
-#define IQS5XX_MAJOR_VER_MIN	2

 #define IQS5XX_SHOW_RESET	BIT(7)
 #define IQS5XX_ACK_RESET	BIT(7)
@@ -87,7 +84,6 @@
 #define IQS5XX_BL_CMD_CRC	0x03
 #define IQS5XX_BL_BLK_LEN_MAX	64
 #define IQS5XX_BL_ID		0x0200
-#define IQS5XX_BL_STATUS_AVAIL	0xA5
 #define IQS5XX_BL_STATUS_NONE	0xEE
 #define IQS5XX_BL_CRC_PASS	0x00
 #define IQS5XX_BL_CRC_FAIL	0x01
@@ -573,7 +569,7 @@ static int iqs5xx_dev_init(struct i2c_client *client)
 	 * the missing zero is prepended).
 	 */
 	buf[0] = 0;
-	dev_id_info = (struct iqs5xx_dev_id_info *)&buf[(buf[1] > 0) ? 0 : 1];
+	dev_id_info = (struct iqs5xx_dev_id_info *)&buf[buf[1] ? 0 : 1];

 	switch (be16_to_cpu(dev_id_info->prod_num)) {
 	case IQS5XX_PROD_NUM_IQS550:
@@ -586,36 +582,16 @@ static int iqs5xx_dev_init(struct i2c_client *client)
 		return -EINVAL;
 	}

-	switch (be16_to_cpu(dev_id_info->proj_num)) {
-	case IQS5XX_PROJ_NUM_A000:
-		dev_err(&client->dev, "Unsupported project number: %u\n",
-			be16_to_cpu(dev_id_info->proj_num));
-		return iqs5xx_bl_open(client);
-	case IQS5XX_PROJ_NUM_B000:
-		break;
-	default:
-		dev_err(&client->dev, "Unrecognized project number: %u\n",
-			be16_to_cpu(dev_id_info->proj_num));
-		return -EINVAL;
-	}
-
-	if (dev_id_info->major_ver < IQS5XX_MAJOR_VER_MIN) {
-		dev_err(&client->dev, "Unsupported major version: %u\n",
-			dev_id_info->major_ver);
+	/*
+	 * With the product number recognized yet shifted by one byte, open the
+	 * bootloader and wait for user space to convert the A000 device into a
+	 * B000 device via new firmware.
+	 */
+	if (buf[1]) {
+		dev_err(&client->dev, "Opening bootloader for A000 device\n");
 		return iqs5xx_bl_open(client);
 	}

-	switch (dev_id_info->bl_status) {
-	case IQS5XX_BL_STATUS_AVAIL:
-	case IQS5XX_BL_STATUS_NONE:
-		break;
-	default:
-		dev_err(&client->dev,
-			"Unrecognized bootloader status: 0x%02X\n",
-			dev_id_info->bl_status);
-		return -EINVAL;
-	}
-
 	error = iqs5xx_read_burst(client, IQS5XX_EXP_FILE,
 				  iqs5xx->exp_file, sizeof(iqs5xx->exp_file));
 	if (error)
--
2.17.1


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v2 5/9] Input: iqs5xx - close bootloader using hardware reset
  2021-03-13 19:12 [PATCH v2 0/9] Input: iqs5xx - more enhancements and optimizations Jeff LaBundy
                   ` (3 preceding siblings ...)
  2021-03-13 19:12 ` [PATCH v2 4/9] Input: iqs5xx - remove superfluous revision validation Jeff LaBundy
@ 2021-03-13 19:12 ` Jeff LaBundy
  2021-03-22  4:04   ` Dmitry Torokhov
  2021-03-13 19:12 ` [PATCH v2 6/9] Input: iqs5xx - prevent interrupt storm during removal Jeff LaBundy
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 19+ messages in thread
From: Jeff LaBundy @ 2021-03-13 19:12 UTC (permalink / raw)
  To: dmitry.torokhov, robh+dt; +Cc: linux-input, devicetree, Jeff LaBundy

The bootloader can be closed using the 'execute' command (0x02) or
hardware reset. Rather than using the former option for successful
firmware update procedures and reserving the latter for recovering
the device upon failure, simply use hardware reset for all cases.

The post-bootloader initialization delay increases marginally when
triggered by a hardware reset, so increase the wait time to ensure
the device does not subsequently fail to respond.

As part of this change, refactor the return path to avoid an extra
assignment and to make the logic a bit smaller.

Signed-off-by: Jeff LaBundy <jeff@labundy.com>
---
Changes in v2:
 - None

 drivers/input/touchscreen/iqs5xx.c | 24 +++++++-----------------
 1 file changed, 7 insertions(+), 17 deletions(-)

diff --git a/drivers/input/touchscreen/iqs5xx.c b/drivers/input/touchscreen/iqs5xx.c
index 0920516124c7..a990c176abf7 100644
--- a/drivers/input/touchscreen/iqs5xx.c
+++ b/drivers/input/touchscreen/iqs5xx.c
@@ -832,7 +832,7 @@ static int iqs5xx_fw_file_parse(struct i2c_client *client,
 static int iqs5xx_fw_file_write(struct i2c_client *client, const char *fw_file)
 {
 	struct iqs5xx_private *iqs5xx = i2c_get_clientdata(client);
-	int error, error_bl = 0;
+	int error, error_init = 0;
 	u8 *pmap;

 	if (iqs5xx->dev_id_info.bl_status == IQS5XX_BL_STATUS_NONE)
@@ -875,21 +875,14 @@ static int iqs5xx_fw_file_write(struct i2c_client *client, const char *fw_file)
 	error = iqs5xx_bl_verify(client, IQS5XX_CSTM,
 				 pmap + IQS5XX_CHKSM_LEN + IQS5XX_APP_LEN,
 				 IQS5XX_CSTM_LEN);
-	if (error)
-		goto err_reset;
-
-	error = iqs5xx_bl_cmd(client, IQS5XX_BL_CMD_EXEC, 0);

 err_reset:
-	if (error) {
-		iqs5xx_reset(client);
-		usleep_range(10000, 10100);
-	}
+	iqs5xx_reset(client);
+	usleep_range(15000, 15100);

-	error_bl = error;
-	error = iqs5xx_dev_init(client);
-	if (!error && !iqs5xx->dev_id_info.bl_status)
-		error = -EINVAL;
+	error_init = iqs5xx_dev_init(client);
+	if (!iqs5xx->dev_id_info.bl_status)
+		error_init = error_init ? : -EINVAL;

 	enable_irq(client->irq);

@@ -898,10 +891,7 @@ static int iqs5xx_fw_file_write(struct i2c_client *client, const char *fw_file)
 err_kfree:
 	kfree(pmap);

-	if (error_bl)
-		return error_bl;
-
-	return error;
+	return error ? : error_init;
 }

 static ssize_t fw_file_store(struct device *dev,
--
2.17.1


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v2 6/9] Input: iqs5xx - prevent interrupt storm during removal
  2021-03-13 19:12 [PATCH v2 0/9] Input: iqs5xx - more enhancements and optimizations Jeff LaBundy
                   ` (4 preceding siblings ...)
  2021-03-13 19:12 ` [PATCH v2 5/9] Input: iqs5xx - close bootloader using hardware reset Jeff LaBundy
@ 2021-03-13 19:12 ` Jeff LaBundy
  2021-03-14  6:21   ` Dmitry Torokhov
  2021-03-13 19:12 ` [PATCH v2 7/9] Input: iqs5xx - suspend or resume regardless of users Jeff LaBundy
                   ` (2 subsequent siblings)
  8 siblings, 1 reply; 19+ messages in thread
From: Jeff LaBundy @ 2021-03-13 19:12 UTC (permalink / raw)
  To: dmitry.torokhov, robh+dt; +Cc: linux-input, devicetree, Jeff LaBundy

Unsolicited I2C communication causes the device to assert an interrupt; as
such the IRQ is disabled before any registers are written in iqs5xx_open()
and iqs5xx_close().

After the driver is unloaded, however, i2c_device_remove() sets the IRQ to
zero before any handlers may call input_close_device() while the device is
unregistered. This keeps iqs5xx_close() from disabling the IRQ, leading to
an interrupt storm during removal.

Placing input_register_device() in front of devm_request_threaded_irq() to
free the IRQ before iqs5xx_close() is called does not cover the case where
firmware is updated at the factory and the input device is registered well
after the driver has already probed.

The solution, therefore, is to remove the open and close callbacks as they
do not buy much in the first place. The device already starts in an active
state, then drops into a low-power mode based on activity.

As an added benefit, this change allows the 250-ms delay in initialization
to be removed as iqs5xx_open() no longer follows immediately. Instead, the
delay is replaced with a mere 50-us delay which allows the interrupt to be
deasserted before the handler is registered.

Signed-off-by: Jeff LaBundy <jeff@labundy.com>
---
Changes in v2:
 - None

 drivers/input/touchscreen/iqs5xx.c | 25 +------------------------
 1 file changed, 1 insertion(+), 24 deletions(-)

diff --git a/drivers/input/touchscreen/iqs5xx.c b/drivers/input/touchscreen/iqs5xx.c
index a990c176abf7..350466ff6bbd 100644
--- a/drivers/input/touchscreen/iqs5xx.c
+++ b/drivers/input/touchscreen/iqs5xx.c
@@ -468,20 +468,6 @@ static int iqs5xx_set_state(struct i2c_client *client, u8 state)
 	return error2;
 }

-static int iqs5xx_open(struct input_dev *input)
-{
-	struct iqs5xx_private *iqs5xx = input_get_drvdata(input);
-
-	return iqs5xx_set_state(iqs5xx->client, IQS5XX_RESUME);
-}
-
-static void iqs5xx_close(struct input_dev *input)
-{
-	struct iqs5xx_private *iqs5xx = input_get_drvdata(input);
-
-	iqs5xx_set_state(iqs5xx->client, IQS5XX_SUSPEND);
-}
-
 static int iqs5xx_axis_init(struct i2c_client *client)
 {
 	struct iqs5xx_private *iqs5xx = i2c_get_clientdata(client);
@@ -497,10 +483,7 @@ static int iqs5xx_axis_init(struct i2c_client *client)

 		input->name = client->name;
 		input->id.bustype = BUS_I2C;
-		input->open = iqs5xx_open;
-		input->close = iqs5xx_close;

-		input_set_drvdata(input, iqs5xx);
 		iqs5xx->input = input;
 	}

@@ -622,13 +605,7 @@ static int iqs5xx_dev_init(struct i2c_client *client)

 	iqs5xx->dev_id_info = *dev_id_info;

-	/*
-	 * The following delay allows ATI to complete before the open and close
-	 * callbacks are free to elicit I2C communication. Any attempts to read
-	 * from or write to the device during this time may face extended clock
-	 * stretching and prompt the I2C controller to report an error.
-	 */
-	msleep(250);
+	usleep_range(50, 100);

 	return 0;
 }
--
2.17.1


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v2 7/9] Input: iqs5xx - suspend or resume regardless of users
  2021-03-13 19:12 [PATCH v2 0/9] Input: iqs5xx - more enhancements and optimizations Jeff LaBundy
                   ` (5 preceding siblings ...)
  2021-03-13 19:12 ` [PATCH v2 6/9] Input: iqs5xx - prevent interrupt storm during removal Jeff LaBundy
@ 2021-03-13 19:12 ` Jeff LaBundy
  2021-03-13 19:12 ` [PATCH v2 8/9] Input: iqs5xx - make reset GPIO optional Jeff LaBundy
  2021-03-13 19:12 ` [PATCH v2 9/9] dt-bindings: input: iqs5xx: Convert to YAML Jeff LaBundy
  8 siblings, 0 replies; 19+ messages in thread
From: Jeff LaBundy @ 2021-03-13 19:12 UTC (permalink / raw)
  To: dmitry.torokhov, robh+dt; +Cc: linux-input, devicetree, Jeff LaBundy

The device should be allowed to enter its lowest-power state during
suspend, even if there are no users. Therefore, drop the check from
iqs5xx_suspend().

It follows that the same check must be removed from iqs5xx_resume()
since users are not guaranteed to be present upon resume, and there
would be no way to power the device back up.

This change makes iqs5xx_suspend() and iqs5xx_resume() both smaller
and easier to follow. And because these are the only functions that
call iqs5xx_set_state() now, call device_may_wakeup() from there to
avoid duplicate logic.

While here, collapse the return path for iqs5xx_set_state() to save
a few lines of code.

Signed-off-by: Jeff LaBundy <jeff@labundy.com>
---
Changes in v2:
 - None

 drivers/input/touchscreen/iqs5xx.c | 41 +++---------------------------
 1 file changed, 4 insertions(+), 37 deletions(-)

diff --git a/drivers/input/touchscreen/iqs5xx.c b/drivers/input/touchscreen/iqs5xx.c
index 350466ff6bbd..180d2618d8c6 100644
--- a/drivers/input/touchscreen/iqs5xx.c
+++ b/drivers/input/touchscreen/iqs5xx.c
@@ -39,9 +39,7 @@

 #define IQS5XX_SHOW_RESET	BIT(7)
 #define IQS5XX_ACK_RESET	BIT(7)
-
 #define IQS5XX_SUSPEND		BIT(0)
-#define IQS5XX_RESUME		0

 #define IQS5XX_SETUP_COMPLETE	BIT(6)
 #define IQS5XX_WDT		BIT(5)
@@ -442,7 +440,7 @@ static int iqs5xx_set_state(struct i2c_client *client, u8 state)
 	struct iqs5xx_private *iqs5xx = i2c_get_clientdata(client);
 	int error1, error2;

-	if (!iqs5xx->dev_id_info.bl_status)
+	if (!iqs5xx->dev_id_info.bl_status || device_may_wakeup(&client->dev))
 		return 0;

 	mutex_lock(&iqs5xx->lock);
@@ -462,10 +460,7 @@ static int iqs5xx_set_state(struct i2c_client *client, u8 state)

 	mutex_unlock(&iqs5xx->lock);

-	if (error1)
-		return error1;
-
-	return error2;
+	return error1 ? : error2;
 }

 static int iqs5xx_axis_init(struct i2c_client *client)
@@ -946,40 +941,12 @@ static const struct attribute_group iqs5xx_attr_group = {

 static int __maybe_unused iqs5xx_suspend(struct device *dev)
 {
-	struct iqs5xx_private *iqs5xx = dev_get_drvdata(dev);
-	struct input_dev *input = iqs5xx->input;
-	int error = 0;
-
-	if (!input || device_may_wakeup(dev))
-		return error;
-
-	mutex_lock(&input->mutex);
-
-	if (input_device_enabled(input))
-		error = iqs5xx_set_state(iqs5xx->client, IQS5XX_SUSPEND);
-
-	mutex_unlock(&input->mutex);
-
-	return error;
+	return iqs5xx_set_state(to_i2c_client(dev), IQS5XX_SUSPEND);
 }

 static int __maybe_unused iqs5xx_resume(struct device *dev)
 {
-	struct iqs5xx_private *iqs5xx = dev_get_drvdata(dev);
-	struct input_dev *input = iqs5xx->input;
-	int error = 0;
-
-	if (!input || device_may_wakeup(dev))
-		return error;
-
-	mutex_lock(&input->mutex);
-
-	if (input_device_enabled(input))
-		error = iqs5xx_set_state(iqs5xx->client, IQS5XX_RESUME);
-
-	mutex_unlock(&input->mutex);
-
-	return error;
+	return iqs5xx_set_state(to_i2c_client(dev), 0);
 }

 static SIMPLE_DEV_PM_OPS(iqs5xx_pm, iqs5xx_suspend, iqs5xx_resume);
--
2.17.1


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v2 8/9] Input: iqs5xx - make reset GPIO optional
  2021-03-13 19:12 [PATCH v2 0/9] Input: iqs5xx - more enhancements and optimizations Jeff LaBundy
                   ` (6 preceding siblings ...)
  2021-03-13 19:12 ` [PATCH v2 7/9] Input: iqs5xx - suspend or resume regardless of users Jeff LaBundy
@ 2021-03-13 19:12 ` Jeff LaBundy
  2021-03-22  4:06   ` Dmitry Torokhov
  2021-03-13 19:12 ` [PATCH v2 9/9] dt-bindings: input: iqs5xx: Convert to YAML Jeff LaBundy
  8 siblings, 1 reply; 19+ messages in thread
From: Jeff LaBundy @ 2021-03-13 19:12 UTC (permalink / raw)
  To: dmitry.torokhov, robh+dt; +Cc: linux-input, devicetree, Jeff LaBundy

The device's hardware reset pin is only required if the platform
must be able to update the device's firmware.

As such, demote the reset GPIO to optional in support of devices
that ship with pre-programmed firmware and don't route the reset
pin back to the SoC.

In that case, the 'fw_file' attribute is hidden because there is
no way to open the bootloader. The logic is extended to the case
in which the device does not advertise bootloader support in the
first place.

Last but not least, remove the hardware reset performed at probe
because there is no reason to reset the device manually. A power
on reset function already ensures a clean reset at start-up.

Signed-off-by: Jeff LaBundy <jeff@labundy.com>
---
Changes in v2:
 - None

 drivers/input/touchscreen/iqs5xx.c | 28 ++++++++++++++++++----------
 1 file changed, 18 insertions(+), 10 deletions(-)

diff --git a/drivers/input/touchscreen/iqs5xx.c b/drivers/input/touchscreen/iqs5xx.c
index 180d2618d8c6..6e53b455bfb9 100644
--- a/drivers/input/touchscreen/iqs5xx.c
+++ b/drivers/input/touchscreen/iqs5xx.c
@@ -807,9 +807,6 @@ static int iqs5xx_fw_file_write(struct i2c_client *client, const char *fw_file)
 	int error, error_init = 0;
 	u8 *pmap;

-	if (iqs5xx->dev_id_info.bl_status == IQS5XX_BL_STATUS_NONE)
-		return -EPERM;
-
 	pmap = kzalloc(IQS5XX_PMAP_LEN, GFP_KERNEL);
 	if (!pmap)
 		return -ENOMEM;
@@ -929,12 +926,21 @@ static ssize_t fw_info_show(struct device *dev,
 static DEVICE_ATTR_WO(fw_file);
 static DEVICE_ATTR_RO(fw_info);

-static struct attribute *iqs5xx_attrs[] = {
+static struct attribute *iqs5xx_attrs_bl[] = {
 	&dev_attr_fw_file.attr,
 	&dev_attr_fw_info.attr,
 	NULL,
 };

+static struct attribute *iqs5xx_attrs[] = {
+	&dev_attr_fw_info.attr,
+	NULL,
+};
+
+static const struct attribute_group iqs5xx_attr_group_bl = {
+	.attrs = iqs5xx_attrs_bl,
+};
+
 static const struct attribute_group iqs5xx_attr_group = {
 	.attrs = iqs5xx_attrs,
 };
@@ -954,6 +960,7 @@ static SIMPLE_DEV_PM_OPS(iqs5xx_pm, iqs5xx_suspend, iqs5xx_resume);
 static int iqs5xx_probe(struct i2c_client *client,
 			const struct i2c_device_id *id)
 {
+	const struct attribute_group *attr_group = &iqs5xx_attr_group;
 	struct iqs5xx_private *iqs5xx;
 	int error;

@@ -964,8 +971,8 @@ static int iqs5xx_probe(struct i2c_client *client,
 	i2c_set_clientdata(client, iqs5xx);
 	iqs5xx->client = client;

-	iqs5xx->reset_gpio = devm_gpiod_get(&client->dev,
-					    "reset", GPIOD_OUT_LOW);
+	iqs5xx->reset_gpio = devm_gpiod_get_optional(&client->dev,
+						     "reset", GPIOD_OUT_LOW);
 	if (IS_ERR(iqs5xx->reset_gpio)) {
 		error = PTR_ERR(iqs5xx->reset_gpio);
 		dev_err(&client->dev, "Failed to request GPIO: %d\n", error);
@@ -974,9 +981,6 @@ static int iqs5xx_probe(struct i2c_client *client,

 	mutex_init(&iqs5xx->lock);

-	iqs5xx_reset(client);
-	usleep_range(10000, 10100);
-
 	error = iqs5xx_dev_init(client);
 	if (error)
 		return error;
@@ -989,7 +993,11 @@ static int iqs5xx_probe(struct i2c_client *client,
 		return error;
 	}

-	error = devm_device_add_group(&client->dev, &iqs5xx_attr_group);
+	if (iqs5xx->reset_gpio &&
+	    iqs5xx->dev_id_info.bl_status != IQS5XX_BL_STATUS_NONE)
+		attr_group = &iqs5xx_attr_group_bl;
+
+	error = devm_device_add_group(&client->dev, attr_group);
 	if (error) {
 		dev_err(&client->dev, "Failed to add attributes: %d\n", error);
 		return error;
--
2.17.1


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v2 9/9] dt-bindings: input: iqs5xx: Convert to YAML
  2021-03-13 19:12 [PATCH v2 0/9] Input: iqs5xx - more enhancements and optimizations Jeff LaBundy
                   ` (7 preceding siblings ...)
  2021-03-13 19:12 ` [PATCH v2 8/9] Input: iqs5xx - make reset GPIO optional Jeff LaBundy
@ 2021-03-13 19:12 ` Jeff LaBundy
  8 siblings, 0 replies; 19+ messages in thread
From: Jeff LaBundy @ 2021-03-13 19:12 UTC (permalink / raw)
  To: dmitry.torokhov, robh+dt; +Cc: linux-input, devicetree, Jeff LaBundy

This patch converts the legacy text-based binding document to YAML
format. Extraneous details and touchscreen properties that weren't
actually supported have been dropped.

The reset GPIO has since been made optional in the driver; this is
now reflected here as well.

Signed-off-by: Jeff LaBundy <jeff@labundy.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
Changes in v2:
 - Added vendor prefix to filename and $id
 - Added Reviewed-by trailer

 .../input/touchscreen/azoteq,iqs5xx.yaml      | 75 +++++++++++++++++
 .../bindings/input/touchscreen/iqs5xx.txt     | 80 -------------------
 2 files changed, 75 insertions(+), 80 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/input/touchscreen/azoteq,iqs5xx.yaml
 delete mode 100644 Documentation/devicetree/bindings/input/touchscreen/iqs5xx.txt

diff --git a/Documentation/devicetree/bindings/input/touchscreen/azoteq,iqs5xx.yaml b/Documentation/devicetree/bindings/input/touchscreen/azoteq,iqs5xx.yaml
new file mode 100644
index 000000000000..b5f377215c09
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/azoteq,iqs5xx.yaml
@@ -0,0 +1,75 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/input/touchscreen/azoteq,iqs5xx.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Azoteq IQS550/572/525 Trackpad/Touchscreen Controller
+
+maintainers:
+  - Jeff LaBundy <jeff@labundy.com>
+
+description: |
+  The Azoteq IQS550, IQS572 and IQS525 trackpad and touchscreen controllers
+  employ projected-capacitance sensing and can track up to five independent
+  contacts.
+
+  Link to datasheet: https://www.azoteq.com/
+
+allOf:
+  - $ref: touchscreen.yaml#
+
+properties:
+  compatible:
+    enum:
+      - azoteq,iqs550
+      - azoteq,iqs572
+      - azoteq,iqs525
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  reset-gpios:
+    maxItems: 1
+
+  wakeup-source: true
+
+  touchscreen-size-x: true
+  touchscreen-size-y: true
+  touchscreen-inverted-x: true
+  touchscreen-inverted-y: true
+  touchscreen-swapped-x-y: true
+
+required:
+  - compatible
+  - reg
+  - interrupts
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+    #include <dt-bindings/interrupt-controller/irq.h>
+
+    i2c {
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            touchscreen@74 {
+                    compatible = "azoteq,iqs550";
+                    reg = <0x74>;
+                    interrupt-parent = <&gpio>;
+                    interrupts = <27 IRQ_TYPE_LEVEL_HIGH>;
+                    reset-gpios = <&gpio 22 (GPIO_ACTIVE_LOW |
+                                             GPIO_PUSH_PULL)>;
+
+                    touchscreen-size-x = <800>;
+                    touchscreen-size-y = <480>;
+            };
+    };
+
+...
diff --git a/Documentation/devicetree/bindings/input/touchscreen/iqs5xx.txt b/Documentation/devicetree/bindings/input/touchscreen/iqs5xx.txt
deleted file mode 100644
index efa0820e2469..000000000000
--- a/Documentation/devicetree/bindings/input/touchscreen/iqs5xx.txt
+++ /dev/null
@@ -1,80 +0,0 @@
-Azoteq IQS550/572/525 Trackpad/Touchscreen Controller
-
-Required properties:
-
-- compatible			: Must be equal to one of the following:
-				  "azoteq,iqs550"
-				  "azoteq,iqs572"
-				  "azoteq,iqs525"
-
-- reg				: I2C slave address for the device.
-
-- interrupts			: GPIO to which the device's active-high RDY
-				  output is connected (see [0]).
-
-- reset-gpios			: GPIO to which the device's active-low NRST
-				  input is connected (see [1]).
-
-Optional properties:
-
-- touchscreen-min-x		: See [2].
-
-- touchscreen-min-y		: See [2].
-
-- touchscreen-size-x		: See [2]. If this property is omitted, the
-				  maximum x-coordinate is specified by the
-				  device's "X Resolution" register.
-
-- touchscreen-size-y		: See [2]. If this property is omitted, the
-				  maximum y-coordinate is specified by the
-				  device's "Y Resolution" register.
-
-- touchscreen-max-pressure	: See [2]. Pressure is expressed as the sum of
-				  the deltas across all channels impacted by a
-				  touch event. A channel's delta is calculated
-				  as its count value minus a reference, where
-				  the count value is inversely proportional to
-				  the channel's capacitance.
-
-- touchscreen-fuzz-x		: See [2].
-
-- touchscreen-fuzz-y		: See [2].
-
-- touchscreen-fuzz-pressure	: See [2].
-
-- touchscreen-inverted-x	: See [2]. Inversion is applied relative to that
-				  which may already be specified by the device's
-				  FLIP_X and FLIP_Y register fields.
-
-- touchscreen-inverted-y	: See [2]. Inversion is applied relative to that
-				  which may already be specified by the device's
-				  FLIP_X and FLIP_Y register fields.
-
-- touchscreen-swapped-x-y	: See [2]. Swapping is applied relative to that
-				  which may already be specified by the device's
-				  SWITCH_XY_AXIS register field.
-
-[0]: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
-[1]: Documentation/devicetree/bindings/gpio/gpio.txt
-[2]: Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt
-
-Example:
-
-	&i2c1 {
-		/* ... */
-
-		touchscreen@74 {
-			compatible = "azoteq,iqs550";
-			reg = <0x74>;
-			interrupt-parent = <&gpio>;
-			interrupts = <17 4>;
-			reset-gpios = <&gpio 27 1>;
-
-			touchscreen-size-x = <640>;
-			touchscreen-size-y = <480>;
-
-			touchscreen-max-pressure = <16000>;
-		};
-
-		/* ... */
-	};
--
2.17.1


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 6/9] Input: iqs5xx - prevent interrupt storm during removal
  2021-03-13 19:12 ` [PATCH v2 6/9] Input: iqs5xx - prevent interrupt storm during removal Jeff LaBundy
@ 2021-03-14  6:21   ` Dmitry Torokhov
  2021-03-15  3:38     ` Jeff LaBundy
  0 siblings, 1 reply; 19+ messages in thread
From: Dmitry Torokhov @ 2021-03-14  6:21 UTC (permalink / raw)
  To: Jeff LaBundy; +Cc: robh+dt, linux-input, devicetree

Hi Jeff,

On Sat, Mar 13, 2021 at 01:12:33PM -0600, Jeff LaBundy wrote:
> Unsolicited I2C communication causes the device to assert an interrupt; as
> such the IRQ is disabled before any registers are written in iqs5xx_open()
> and iqs5xx_close().
> 
> After the driver is unloaded, however, i2c_device_remove() sets the IRQ to
> zero before any handlers may call input_close_device() while the device is
> unregistered. This keeps iqs5xx_close() from disabling the IRQ, leading to
> an interrupt storm during removal.
> 
> Placing input_register_device() in front of devm_request_threaded_irq() to
> free the IRQ before iqs5xx_close() is called does not cover the case where
> firmware is updated at the factory and the input device is registered well
> after the driver has already probed.
> 
> The solution, therefore, is to remove the open and close callbacks as they
> do not buy much in the first place. The device already starts in an active
> state, then drops into a low-power mode based on activity.

No, this is not the proper solution. We should rather fix i2c bus (and
really all the other buses with non-trivial probe and remove) so that it
is compatible with devres/devm. I wanted to do this for a while and I
guess we really need this. Could you please try the patch below and see
if it fixes your issue?

Thanks.

-- 
Dmitry


i2c: ensure timely release of driver-allocated resources

From: Dmitry Torokhov <dmitry.torokhov@gmail.com>

More and more drivers rely on devres to manage their resources, however if
bus' probe() and release() are not trivial and control some of resources as
well (for example enable or disable clocks, or attach device to a power
domain, we need to make sure that driver-allocated resources are released
immediately after driver's remove() method returns, and not postponed until
driver core gets around to releasing resources. To fix that we open a new
devres group before calling driver's probe() and explicitly release it when
we return from driver's remove().

Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
 drivers/i2c/i2c-core-base.c |   19 ++++++++++++++++++-
 include/linux/i2c.h         |    3 +++
 2 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
index 63ebf722a424..b8a96db2c191 100644
--- a/drivers/i2c/i2c-core-base.c
+++ b/drivers/i2c/i2c-core-base.c
@@ -518,6 +518,11 @@ static int i2c_device_probe(struct device *dev)
 	if (status)
 		goto err_clear_wakeup_irq;
 
+	client->devres_group_id = devres_open_group(&client->dev, NULL,
+						    GFP_KERNEL);
+	if (!client->devres_group_id)
+		goto err_detach_pm_domain;
+
 	/*
 	 * When there are no more users of probe(),
 	 * rename probe_new to probe.
@@ -530,11 +535,21 @@ static int i2c_device_probe(struct device *dev)
 	else
 		status = -EINVAL;
 
+	/*
+	 * Note that we are not closing the devres group opened above so
+	 * even resources that were attached to the device after probe is
+	 * run are released when i2c_device_remove() is executed. This is
+	 * needed as some drivers would allocate additional resources,
+	 * for example when updating firmware.
+	 */
+
 	if (status)
-		goto err_detach_pm_domain;
+		goto err_release_driver_resources;
 
 	return 0;
 
+err_release_driver_resources:
+	devres_release_group(&client->dev, client->devres_group_id);
 err_detach_pm_domain:
 	dev_pm_domain_detach(&client->dev, true);
 err_clear_wakeup_irq:
@@ -563,6 +578,8 @@ static int i2c_device_remove(struct device *dev)
 			dev_warn(dev, "remove failed (%pe), will be ignored\n", ERR_PTR(status));
 	}
 
+	devres_release_group(&client->dev, client->devres_group_id);
+
 	dev_pm_domain_detach(&client->dev, true);
 
 	dev_pm_clear_wake_irq(&client->dev);
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index 56622658b215..5d1f11c0deaa 100644
--- a/include/linux/i2c.h
+++ b/include/linux/i2c.h
@@ -306,6 +306,8 @@ struct i2c_driver {
  *	userspace_devices list
  * @slave_cb: Callback when I2C slave mode of an adapter is used. The adapter
  *	calls it to pass on slave events to the slave driver.
+ * @devres_group_id: id of the devres group that will be created for resources
+ *	acquired when probing this device.
  *
  * An i2c_client identifies a single device (i.e. chip) connected to an
  * i2c bus. The behaviour exposed to Linux is defined by the driver
@@ -334,6 +336,7 @@ struct i2c_client {
 #if IS_ENABLED(CONFIG_I2C_SLAVE)
 	i2c_slave_cb_t slave_cb;	/* callback for slave mode	*/
 #endif
+	void *devres_group_id;		/* ID of probe devres group	*/
 };
 #define to_i2c_client(d) container_of(d, struct i2c_client, dev)
 

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 6/9] Input: iqs5xx - prevent interrupt storm during removal
  2021-03-14  6:21   ` Dmitry Torokhov
@ 2021-03-15  3:38     ` Jeff LaBundy
  0 siblings, 0 replies; 19+ messages in thread
From: Jeff LaBundy @ 2021-03-15  3:38 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: robh+dt, linux-input, devicetree

Hi Dmitry,

On Sat, Mar 13, 2021 at 10:21:27PM -0800, Dmitry Torokhov wrote:
> Hi Jeff,
> 
> On Sat, Mar 13, 2021 at 01:12:33PM -0600, Jeff LaBundy wrote:
> > Unsolicited I2C communication causes the device to assert an interrupt; as
> > such the IRQ is disabled before any registers are written in iqs5xx_open()
> > and iqs5xx_close().
> > 
> > After the driver is unloaded, however, i2c_device_remove() sets the IRQ to
> > zero before any handlers may call input_close_device() while the device is
> > unregistered. This keeps iqs5xx_close() from disabling the IRQ, leading to
> > an interrupt storm during removal.
> > 
> > Placing input_register_device() in front of devm_request_threaded_irq() to
> > free the IRQ before iqs5xx_close() is called does not cover the case where
> > firmware is updated at the factory and the input device is registered well
> > after the driver has already probed.
> > 
> > The solution, therefore, is to remove the open and close callbacks as they
> > do not buy much in the first place. The device already starts in an active
> > state, then drops into a low-power mode based on activity.
> 
> No, this is not the proper solution. We should rather fix i2c bus (and
> really all the other buses with non-trivial probe and remove) so that it
> is compatible with devres/devm. I wanted to do this for a while and I
> guess we really need this. Could you please try the patch below and see
> if it fixes your issue?

Thank you for this suggestion; to be honest I had not considered how other
drivers may suffer a similar fate and I agree with your approach. I tested
your patch and it addresses my issue.

That being said, I would still advocate for this patch because of the other
reasons mentioned: the open/close callbacks do not happen to buy much since
the device effectively "opens" and (almost) "closes" automatically based on
touch events, and getting rid of the callbacks lets probe finish faster and
cleans up the code a bit.

Perhaps as a compromise, I can squash this and the next patch, and speak to
these points in a consolidated commit message?

> 
> Thanks.
> 
> -- 
> Dmitry

Kind regards,
Jeff LaBundy

> 
> 
> i2c: ensure timely release of driver-allocated resources
> 
> From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> 
> More and more drivers rely on devres to manage their resources, however if
> bus' probe() and release() are not trivial and control some of resources as
> well (for example enable or disable clocks, or attach device to a power
> domain, we need to make sure that driver-allocated resources are released
> immediately after driver's remove() method returns, and not postponed until
> driver core gets around to releasing resources. To fix that we open a new
> devres group before calling driver's probe() and explicitly release it when
> we return from driver's remove().
> 
> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>

Tested-by: Jeff LaBundy <jeff@labundy.com>

> ---
>  drivers/i2c/i2c-core-base.c |   19 ++++++++++++++++++-
>  include/linux/i2c.h         |    3 +++
>  2 files changed, 21 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
> index 63ebf722a424..b8a96db2c191 100644
> --- a/drivers/i2c/i2c-core-base.c
> +++ b/drivers/i2c/i2c-core-base.c
> @@ -518,6 +518,11 @@ static int i2c_device_probe(struct device *dev)
>  	if (status)
>  		goto err_clear_wakeup_irq;
>  
> +	client->devres_group_id = devres_open_group(&client->dev, NULL,
> +						    GFP_KERNEL);
> +	if (!client->devres_group_id)
> +		goto err_detach_pm_domain;
> +
>  	/*
>  	 * When there are no more users of probe(),
>  	 * rename probe_new to probe.
> @@ -530,11 +535,21 @@ static int i2c_device_probe(struct device *dev)
>  	else
>  		status = -EINVAL;
>  
> +	/*
> +	 * Note that we are not closing the devres group opened above so
> +	 * even resources that were attached to the device after probe is
> +	 * run are released when i2c_device_remove() is executed. This is
> +	 * needed as some drivers would allocate additional resources,
> +	 * for example when updating firmware.
> +	 */
> +
>  	if (status)
> -		goto err_detach_pm_domain;
> +		goto err_release_driver_resources;
>  
>  	return 0;
>  
> +err_release_driver_resources:
> +	devres_release_group(&client->dev, client->devres_group_id);
>  err_detach_pm_domain:
>  	dev_pm_domain_detach(&client->dev, true);
>  err_clear_wakeup_irq:
> @@ -563,6 +578,8 @@ static int i2c_device_remove(struct device *dev)
>  			dev_warn(dev, "remove failed (%pe), will be ignored\n", ERR_PTR(status));
>  	}
>  
> +	devres_release_group(&client->dev, client->devres_group_id);
> +
>  	dev_pm_domain_detach(&client->dev, true);
>  
>  	dev_pm_clear_wake_irq(&client->dev);
> diff --git a/include/linux/i2c.h b/include/linux/i2c.h
> index 56622658b215..5d1f11c0deaa 100644
> --- a/include/linux/i2c.h
> +++ b/include/linux/i2c.h
> @@ -306,6 +306,8 @@ struct i2c_driver {
>   *	userspace_devices list
>   * @slave_cb: Callback when I2C slave mode of an adapter is used. The adapter
>   *	calls it to pass on slave events to the slave driver.
> + * @devres_group_id: id of the devres group that will be created for resources
> + *	acquired when probing this device.
>   *
>   * An i2c_client identifies a single device (i.e. chip) connected to an
>   * i2c bus. The behaviour exposed to Linux is defined by the driver
> @@ -334,6 +336,7 @@ struct i2c_client {
>  #if IS_ENABLED(CONFIG_I2C_SLAVE)
>  	i2c_slave_cb_t slave_cb;	/* callback for slave mode	*/
>  #endif
> +	void *devres_group_id;		/* ID of probe devres group	*/
>  };
>  #define to_i2c_client(d) container_of(d, struct i2c_client, dev)
>  

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 1/9] Input: iqs5xx - update vendor's URL
  2021-03-13 19:12 ` [PATCH v2 1/9] Input: iqs5xx - update vendor's URL Jeff LaBundy
@ 2021-03-22  4:00   ` Dmitry Torokhov
  0 siblings, 0 replies; 19+ messages in thread
From: Dmitry Torokhov @ 2021-03-22  4:00 UTC (permalink / raw)
  To: Jeff LaBundy; +Cc: robh+dt, linux-input, devicetree

On Sat, Mar 13, 2021 at 01:12:28PM -0600, Jeff LaBundy wrote:
> Replace 'http' with 'https' and correct the spelling of the nearby
> word 'datasheet'.
> 
> Signed-off-by: Jeff LaBundy <jeff@labundy.com>

Applied, thank you.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 2/9] Input: iqs5xx - optimize axis definition and validation
  2021-03-13 19:12 ` [PATCH v2 2/9] Input: iqs5xx - optimize axis definition and validation Jeff LaBundy
@ 2021-03-22  4:02   ` Dmitry Torokhov
  0 siblings, 0 replies; 19+ messages in thread
From: Dmitry Torokhov @ 2021-03-22  4:02 UTC (permalink / raw)
  To: Jeff LaBundy; +Cc: robh+dt, linux-input, devicetree

On Sat, Mar 13, 2021 at 01:12:29PM -0600, Jeff LaBundy wrote:
> Set the maximum ABS_MT_PRESSURE value and use the existing U16_MAX
> definition instead of a magic number to validate ABS_MT_POSITION_X
> and ABS_MT_POSITION_Y.
> 
> Also use input_set_abs_params() rather than input_abs_set_max() to
> avoid having to call input_set_capability() separately.
> 
> Signed-off-by: Jeff LaBundy <jeff@labundy.com>

Applied, thank you.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 3/9] Input: iqs5xx - expose firmware revision to user space
  2021-03-13 19:12 ` [PATCH v2 3/9] Input: iqs5xx - expose firmware revision to user space Jeff LaBundy
@ 2021-03-22  4:02   ` Dmitry Torokhov
  0 siblings, 0 replies; 19+ messages in thread
From: Dmitry Torokhov @ 2021-03-22  4:02 UTC (permalink / raw)
  To: Jeff LaBundy; +Cc: robh+dt, linux-input, devicetree

On Sat, Mar 13, 2021 at 01:12:30PM -0600, Jeff LaBundy wrote:
> Add the read-only 'fw_info' attribute which reports information
> about the device's firmware in the following format:
> 
> a.b.c.d:e.f
> 
> Where:
> 
> a = Product number (e.g. 40 for IQS550)
> b = Project number (e.g. 15)
> c = Firmware revision (major)
> d = Firmware revision (minor)
> e = Customer-assigned exported file version (major)
> f = Customer-assigned exported file version (minor)
> 
> As part of the corresponding rework to uses of 'bl_status', the
> IQS5XX_BL_STATUS_RESET definition is dropped with 0 used in its
> place instead.
> 
> Signed-off-by: Jeff LaBundy <jeff@labundy.com>

Applied, thank you.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 4/9] Input: iqs5xx - remove superfluous revision validation
  2021-03-13 19:12 ` [PATCH v2 4/9] Input: iqs5xx - remove superfluous revision validation Jeff LaBundy
@ 2021-03-22  4:03   ` Dmitry Torokhov
  0 siblings, 0 replies; 19+ messages in thread
From: Dmitry Torokhov @ 2021-03-22  4:03 UTC (permalink / raw)
  To: Jeff LaBundy; +Cc: robh+dt, linux-input, devicetree

On Sat, Mar 13, 2021 at 01:12:31PM -0600, Jeff LaBundy wrote:
> The vendor-assigned firmware project number is restricted to the
> generic project number (15); however the vendor may assign other
> project numbers to specific applications and customers.
> 
> These custom project numbers may be based on forwards-compatible
> firmware revision 1.x. However, the driver unnecessarily rejects
> anything older than firmware revision 2.0.
> 
> To support other applications, remove these unnecessarily strict
> checks and enter the bootloader only for truly incompatible A000
> devices.
> 
> Signed-off-by: Jeff LaBundy <jeff@labundy.com>

Applied, thank you.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 5/9] Input: iqs5xx - close bootloader using hardware reset
  2021-03-13 19:12 ` [PATCH v2 5/9] Input: iqs5xx - close bootloader using hardware reset Jeff LaBundy
@ 2021-03-22  4:04   ` Dmitry Torokhov
  0 siblings, 0 replies; 19+ messages in thread
From: Dmitry Torokhov @ 2021-03-22  4:04 UTC (permalink / raw)
  To: Jeff LaBundy; +Cc: robh+dt, linux-input, devicetree

On Sat, Mar 13, 2021 at 01:12:32PM -0600, Jeff LaBundy wrote:
> The bootloader can be closed using the 'execute' command (0x02) or
> hardware reset. Rather than using the former option for successful
> firmware update procedures and reserving the latter for recovering
> the device upon failure, simply use hardware reset for all cases.
> 
> The post-bootloader initialization delay increases marginally when
> triggered by a hardware reset, so increase the wait time to ensure
> the device does not subsequently fail to respond.
> 
> As part of this change, refactor the return path to avoid an extra
> assignment and to make the logic a bit smaller.
> 
> Signed-off-by: Jeff LaBundy <jeff@labundy.com>

Applied, thank you.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 8/9] Input: iqs5xx - make reset GPIO optional
  2021-03-13 19:12 ` [PATCH v2 8/9] Input: iqs5xx - make reset GPIO optional Jeff LaBundy
@ 2021-03-22  4:06   ` Dmitry Torokhov
  2021-03-22 15:42     ` Jeff LaBundy
  0 siblings, 1 reply; 19+ messages in thread
From: Dmitry Torokhov @ 2021-03-22  4:06 UTC (permalink / raw)
  To: Jeff LaBundy; +Cc: robh+dt, linux-input, devicetree

Hi Jeff,

On Sat, Mar 13, 2021 at 01:12:35PM -0600, Jeff LaBundy wrote:
> @@ -989,7 +993,11 @@ static int iqs5xx_probe(struct i2c_client *client,
>  		return error;
>  	}
> 
> -	error = devm_device_add_group(&client->dev, &iqs5xx_attr_group);
> +	if (iqs5xx->reset_gpio &&
> +	    iqs5xx->dev_id_info.bl_status != IQS5XX_BL_STATUS_NONE)
> +		attr_group = &iqs5xx_attr_group_bl;

I would prefer if we defined is_visible for the attribute instead of
conditional registration.

> +
> +	error = devm_device_add_group(&client->dev, attr_group);
>  	if (error) {
>  		dev_err(&client->dev, "Failed to add attributes: %d\n", error);
>  		return error;
> --
> 2.17.1
> 

Thanks.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 8/9] Input: iqs5xx - make reset GPIO optional
  2021-03-22  4:06   ` Dmitry Torokhov
@ 2021-03-22 15:42     ` Jeff LaBundy
  0 siblings, 0 replies; 19+ messages in thread
From: Jeff LaBundy @ 2021-03-22 15:42 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: robh+dt, linux-input, devicetree

Hi Dmitry,

On Sun, Mar 21, 2021 at 09:06:55PM -0700, Dmitry Torokhov wrote:
> Hi Jeff,
> 
> On Sat, Mar 13, 2021 at 01:12:35PM -0600, Jeff LaBundy wrote:
> > @@ -989,7 +993,11 @@ static int iqs5xx_probe(struct i2c_client *client,
> >  		return error;
> >  	}
> > 
> > -	error = devm_device_add_group(&client->dev, &iqs5xx_attr_group);
> > +	if (iqs5xx->reset_gpio &&
> > +	    iqs5xx->dev_id_info.bl_status != IQS5XX_BL_STATUS_NONE)
> > +		attr_group = &iqs5xx_attr_group_bl;
> 
> I would prefer if we defined is_visible for the attribute instead of
> conditional registration.

Thank you for pointing me to that option; it's much cleaner and I'll
send a v3 with an is_visible callback.

> 
> > +
> > +	error = devm_device_add_group(&client->dev, attr_group);
> >  	if (error) {
> >  		dev_err(&client->dev, "Failed to add attributes: %d\n", error);
> >  		return error;
> > --
> > 2.17.1
> > 
> 
> Thanks.
> 
> -- 
> Dmitry

Kind regards,
Jeff LaBundy

^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2021-03-22 15:43 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-03-13 19:12 [PATCH v2 0/9] Input: iqs5xx - more enhancements and optimizations Jeff LaBundy
2021-03-13 19:12 ` [PATCH v2 1/9] Input: iqs5xx - update vendor's URL Jeff LaBundy
2021-03-22  4:00   ` Dmitry Torokhov
2021-03-13 19:12 ` [PATCH v2 2/9] Input: iqs5xx - optimize axis definition and validation Jeff LaBundy
2021-03-22  4:02   ` Dmitry Torokhov
2021-03-13 19:12 ` [PATCH v2 3/9] Input: iqs5xx - expose firmware revision to user space Jeff LaBundy
2021-03-22  4:02   ` Dmitry Torokhov
2021-03-13 19:12 ` [PATCH v2 4/9] Input: iqs5xx - remove superfluous revision validation Jeff LaBundy
2021-03-22  4:03   ` Dmitry Torokhov
2021-03-13 19:12 ` [PATCH v2 5/9] Input: iqs5xx - close bootloader using hardware reset Jeff LaBundy
2021-03-22  4:04   ` Dmitry Torokhov
2021-03-13 19:12 ` [PATCH v2 6/9] Input: iqs5xx - prevent interrupt storm during removal Jeff LaBundy
2021-03-14  6:21   ` Dmitry Torokhov
2021-03-15  3:38     ` Jeff LaBundy
2021-03-13 19:12 ` [PATCH v2 7/9] Input: iqs5xx - suspend or resume regardless of users Jeff LaBundy
2021-03-13 19:12 ` [PATCH v2 8/9] Input: iqs5xx - make reset GPIO optional Jeff LaBundy
2021-03-22  4:06   ` Dmitry Torokhov
2021-03-22 15:42     ` Jeff LaBundy
2021-03-13 19:12 ` [PATCH v2 9/9] dt-bindings: input: iqs5xx: Convert to YAML Jeff LaBundy

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.