linux-input.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/9] Input: iqs5xx - more enhancements and optimizations
@ 2021-03-05  4:12 Jeff LaBundy
  2021-03-05  4:12 ` [PATCH 1/9] Input: iqs5xx - update vendor's URL Jeff LaBundy
                   ` (8 more replies)
  0 siblings, 9 replies; 11+ messages in thread
From: Jeff LaBundy @ 2021-03-05  4: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

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

--
2.17.1


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

* [PATCH 1/9] Input: iqs5xx - update vendor's URL
  2021-03-05  4:12 [PATCH 0/9] Input: iqs5xx - more enhancements and optimizations Jeff LaBundy
@ 2021-03-05  4:12 ` Jeff LaBundy
  2021-03-05  4:12 ` [PATCH 2/9] Input: iqs5xx - optimize axis definition and validation Jeff LaBundy
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: Jeff LaBundy @ 2021-03-05  4: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>
---
 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] 11+ messages in thread

* [PATCH 2/9] Input: iqs5xx - optimize axis definition and validation
  2021-03-05  4:12 [PATCH 0/9] Input: iqs5xx - more enhancements and optimizations Jeff LaBundy
  2021-03-05  4:12 ` [PATCH 1/9] Input: iqs5xx - update vendor's URL Jeff LaBundy
@ 2021-03-05  4:12 ` Jeff LaBundy
  2021-03-05  4:12 ` [PATCH 3/9] Input: iqs5xx - expose firmware revision to user space Jeff LaBundy
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: Jeff LaBundy @ 2021-03-05  4: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>
---
 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] 11+ messages in thread

* [PATCH 3/9] Input: iqs5xx - expose firmware revision to user space
  2021-03-05  4:12 [PATCH 0/9] Input: iqs5xx - more enhancements and optimizations Jeff LaBundy
  2021-03-05  4:12 ` [PATCH 1/9] Input: iqs5xx - update vendor's URL Jeff LaBundy
  2021-03-05  4:12 ` [PATCH 2/9] Input: iqs5xx - optimize axis definition and validation Jeff LaBundy
@ 2021-03-05  4:12 ` Jeff LaBundy
  2021-03-05  4:12 ` [PATCH 4/9] Input: iqs5xx - remove superfluous revision validation Jeff LaBundy
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: Jeff LaBundy @ 2021-03-05  4: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>
---
 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] 11+ messages in thread

* [PATCH 4/9] Input: iqs5xx - remove superfluous revision validation
  2021-03-05  4:12 [PATCH 0/9] Input: iqs5xx - more enhancements and optimizations Jeff LaBundy
                   ` (2 preceding siblings ...)
  2021-03-05  4:12 ` [PATCH 3/9] Input: iqs5xx - expose firmware revision to user space Jeff LaBundy
@ 2021-03-05  4:12 ` Jeff LaBundy
  2021-03-05  4:12 ` [PATCH 5/9] Input: iqs5xx - close bootloader using hardware reset Jeff LaBundy
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: Jeff LaBundy @ 2021-03-05  4: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>
---
 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] 11+ messages in thread

* [PATCH 5/9] Input: iqs5xx - close bootloader using hardware reset
  2021-03-05  4:12 [PATCH 0/9] Input: iqs5xx - more enhancements and optimizations Jeff LaBundy
                   ` (3 preceding siblings ...)
  2021-03-05  4:12 ` [PATCH 4/9] Input: iqs5xx - remove superfluous revision validation Jeff LaBundy
@ 2021-03-05  4:12 ` Jeff LaBundy
  2021-03-05  4:12 ` [PATCH 6/9] Input: iqs5xx - prevent interrupt storm during removal Jeff LaBundy
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: Jeff LaBundy @ 2021-03-05  4: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>
---
 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] 11+ messages in thread

* [PATCH 6/9] Input: iqs5xx - prevent interrupt storm during removal
  2021-03-05  4:12 [PATCH 0/9] Input: iqs5xx - more enhancements and optimizations Jeff LaBundy
                   ` (4 preceding siblings ...)
  2021-03-05  4:12 ` [PATCH 5/9] Input: iqs5xx - close bootloader using hardware reset Jeff LaBundy
@ 2021-03-05  4:12 ` Jeff LaBundy
  2021-03-05  4:12 ` [PATCH 7/9] Input: iqs5xx - suspend or resume regardless of users Jeff LaBundy
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: Jeff LaBundy @ 2021-03-05  4: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>
---
 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] 11+ messages in thread

* [PATCH 7/9] Input: iqs5xx - suspend or resume regardless of users
  2021-03-05  4:12 [PATCH 0/9] Input: iqs5xx - more enhancements and optimizations Jeff LaBundy
                   ` (5 preceding siblings ...)
  2021-03-05  4:12 ` [PATCH 6/9] Input: iqs5xx - prevent interrupt storm during removal Jeff LaBundy
@ 2021-03-05  4:12 ` Jeff LaBundy
  2021-03-05  4:12 ` [PATCH 8/9] Input: iqs5xx - make reset GPIO optional Jeff LaBundy
  2021-03-05  4:12 ` [PATCH 9/9] dt-bindings: input: iqs5xx: Convert to YAML Jeff LaBundy
  8 siblings, 0 replies; 11+ messages in thread
From: Jeff LaBundy @ 2021-03-05  4: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>
---
 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] 11+ messages in thread

* [PATCH 8/9] Input: iqs5xx - make reset GPIO optional
  2021-03-05  4:12 [PATCH 0/9] Input: iqs5xx - more enhancements and optimizations Jeff LaBundy
                   ` (6 preceding siblings ...)
  2021-03-05  4:12 ` [PATCH 7/9] Input: iqs5xx - suspend or resume regardless of users Jeff LaBundy
@ 2021-03-05  4:12 ` Jeff LaBundy
  2021-03-05  4:12 ` [PATCH 9/9] dt-bindings: input: iqs5xx: Convert to YAML Jeff LaBundy
  8 siblings, 0 replies; 11+ messages in thread
From: Jeff LaBundy @ 2021-03-05  4: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>
---
 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] 11+ messages in thread

* [PATCH 9/9] dt-bindings: input: iqs5xx: Convert to YAML
  2021-03-05  4:12 [PATCH 0/9] Input: iqs5xx - more enhancements and optimizations Jeff LaBundy
                   ` (7 preceding siblings ...)
  2021-03-05  4:12 ` [PATCH 8/9] Input: iqs5xx - make reset GPIO optional Jeff LaBundy
@ 2021-03-05  4:12 ` Jeff LaBundy
  2021-03-08 21:46   ` Rob Herring
  8 siblings, 1 reply; 11+ messages in thread
From: Jeff LaBundy @ 2021-03-05  4: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>
---
 .../bindings/input/touchscreen/iqs5xx.txt     | 80 -------------------
 .../bindings/input/touchscreen/iqs5xx.yaml    | 75 +++++++++++++++++
 2 files changed, 75 insertions(+), 80 deletions(-)
 delete mode 100644 Documentation/devicetree/bindings/input/touchscreen/iqs5xx.txt
 create mode 100644 Documentation/devicetree/bindings/input/touchscreen/iqs5xx.yaml

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>;
-		};
-
-		/* ... */
-	};
diff --git a/Documentation/devicetree/bindings/input/touchscreen/iqs5xx.yaml b/Documentation/devicetree/bindings/input/touchscreen/iqs5xx.yaml
new file mode 100644
index 000000000000..5d802f153a28
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/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/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>;
+            };
+    };
+
+...
-- 
2.17.1


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

* Re: [PATCH 9/9] dt-bindings: input: iqs5xx: Convert to YAML
  2021-03-05  4:12 ` [PATCH 9/9] dt-bindings: input: iqs5xx: Convert to YAML Jeff LaBundy
@ 2021-03-08 21:46   ` Rob Herring
  0 siblings, 0 replies; 11+ messages in thread
From: Rob Herring @ 2021-03-08 21:46 UTC (permalink / raw)
  To: Jeff LaBundy; +Cc: dmitry.torokhov, linux-input, devicetree

On Thu, Mar 04, 2021 at 10:12:36PM -0600, Jeff LaBundy wrote:
> 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>
> ---
>  .../bindings/input/touchscreen/iqs5xx.txt     | 80 -------------------
>  .../bindings/input/touchscreen/iqs5xx.yaml    | 75 +++++++++++++++++

azoteq,iqs5xx.yaml for the filename (and $id).

With that,

Reviewed-by: Rob Herring <robh@kernel.org>

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

end of thread, other threads:[~2021-03-08 21:47 UTC | newest]

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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).