[v1,36/63] Input: atmel_mxt_ts - configure and use gpios as real gpios
diff mbox series

Message ID 20190816083525.19071-2-jiada_wang@mentor.com
State New
Headers show
Series
  • Untitled series #406676
Related show

Commit Message

Jiada Wang Aug. 16, 2019, 8:34 a.m. UTC
From: Kautuk Consul <kautuk_consul@mentor.com>

The upstream Atmel mXT driver implementation seems to handle the
T19 GPIO/PWM object as a key pad. Keys can be defined in the
device tree ("linux,gpio-keymap") and will be transported as key
events to the Linux input device if GPIO state changes.

With our hardware, the GPIO pins of the touch controller are
connected to a PWM/backlight controller and used as supervision
inputs. We like to read the status of the pins by a script or an
application in the sysfs.

Adding newer sysfs entries which shall be placed in the input
class directory eg:
/sys/class/input/input<n>/backlight_error1

Signed-off-by: Kautuk Consul <kautuk_consul@mentor.com>
Signed-off-by: Balasubramani Vivekanandan <balasubramani_vivekanandan@mentor.com>
Signed-off-by: George G. Davis <george_davis@mentor.com>
Signed-off-by: Jiada Wang <jiada_wang@mentor.com>
---
 .../bindings/input/atmel,maxtouch.txt         |  15 +++
 drivers/input/touchscreen/atmel_mxt_ts.c      | 120 ++++++++++++++++++
 2 files changed, 135 insertions(+)

Comments

Dmitry Torokhov Aug. 16, 2019, 5:24 p.m. UTC | #1
On Fri, Aug 16, 2019 at 05:34:58PM +0900, Jiada Wang wrote:
> From: Kautuk Consul <kautuk_consul@mentor.com>
> 
> The upstream Atmel mXT driver implementation seems to handle the
> T19 GPIO/PWM object as a key pad. Keys can be defined in the
> device tree ("linux,gpio-keymap") and will be transported as key
> events to the Linux input device if GPIO state changes.
> 
> With our hardware, the GPIO pins of the touch controller are
> connected to a PWM/backlight controller and used as supervision
> inputs. We like to read the status of the pins by a script or an
> application in the sysfs.
> 
> Adding newer sysfs entries which shall be placed in the input
> class directory eg:
> /sys/class/input/input<n>/backlight_error1

No, if you want to export GPIO lines for external use create a gpiochip
instance and register it with GPIO subsystem. No ad-hoc sysfs please.

Thanks.
Jiada Wang Aug. 23, 2019, 5:16 a.m. UTC | #2
Hi Dmitry

On 2019/08/17 2:24, Dmitry Torokhov wrote:
> On Fri, Aug 16, 2019 at 05:34:58PM +0900, Jiada Wang wrote:
>> From: Kautuk Consul <kautuk_consul@mentor.com>
>>
>> The upstream Atmel mXT driver implementation seems to handle the
>> T19 GPIO/PWM object as a key pad. Keys can be defined in the
>> device tree ("linux,gpio-keymap") and will be transported as key
>> events to the Linux input device if GPIO state changes.
>>
>> With our hardware, the GPIO pins of the touch controller are
>> connected to a PWM/backlight controller and used as supervision
>> inputs. We like to read the status of the pins by a script or an
>> application in the sysfs.
>>
>> Adding newer sysfs entries which shall be placed in the input
>> class directory eg:
>> /sys/class/input/input<n>/backlight_error1
> 
> No, if you want to export GPIO lines for external use create a gpiochip
> instance and register it with GPIO subsystem. No ad-hoc sysfs please.
> 
Agree, I will drop this patch in v2 patch-set

Thanks,
Jiada
> Thanks.
>

Patch
diff mbox series

diff --git a/Documentation/devicetree/bindings/input/atmel,maxtouch.txt b/Documentation/devicetree/bindings/input/atmel,maxtouch.txt
index d7db16920083..7afe12a93202 100644
--- a/Documentation/devicetree/bindings/input/atmel,maxtouch.txt
+++ b/Documentation/devicetree/bindings/input/atmel,maxtouch.txt
@@ -42,6 +42,9 @@  Optional properties for main touchpad device:
 
 - atmel,input_name: Override name of input device from the default.
 
+- atmel,gpios: Specify the GPIO input pins whose status will be read via the
+    /sys/class/input/input<n>/backlight_error<x> sysfs entries.
+
 Example:
 
 	touch@4b {
@@ -49,4 +52,16 @@  Example:
 		reg = <0x4b>;
 		interrupt-parent = <&gpio>;
 		interrupts = <TEGRA_GPIO(W, 3) IRQ_TYPE_LEVEL_LOW>;
+
+		atmel,gpios {
+			backlight_error1 {
+				gpio = <3 GPIO_ACTIVE_HIGH>; /* connected to
+							      * the GPIO3 pin of mXT input */
+			};
+
+			backlight_error2 {
+				gpio = <5 GPIO_ACTIVE_HIGH>; /* connected to
+							      * the GPIO5 pin of mXT input */
+			};
+		};
 	};
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 9cdb7754599c..a8e2b927bb12 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -414,6 +414,15 @@  struct mxt_data {
 
 	/* Indicates whether device is updating configuration */
 	bool updating_config;
+
+	unsigned long gpio_input_pin_status;
+	struct attribute_group gpio_attrs;
+	unsigned long gpio_input_pin_status_default;
+};
+
+struct mxt_gpio_attr {
+	struct device_attribute attr;
+	int bit_index;
 };
 
 struct mxt_vb2_buffer {
@@ -1381,6 +1390,7 @@  static int mxt_proc_message(struct mxt_data *data, u8 *message)
 	} else if (report_id == data->T19_reportid) {
 		mxt_input_button(data, message);
 		data->update_input = true;
+		data->gpio_input_pin_status = message[1];
 	} else if (report_id >= data->T15_reportid_min
 		   && report_id <= data->T15_reportid_max) {
 		mxt_proc_t15_messages(data, message);
@@ -2747,6 +2757,16 @@  static int mxt_initialize_input_device(struct mxt_data *data)
 		goto err_free_mem;
 	}
 
+	if (data->gpio_attrs.attrs) {
+		error = sysfs_create_group(&input_dev->dev.kobj,
+					   &data->gpio_attrs);
+		if (error) {
+			dev_err(dev, "Failure %d creating sysfs group\n",
+				error);
+			goto err_free_mem;
+		}
+	}
+
 	data->input_dev = input_dev;
 
 	return 0;
@@ -3995,10 +4015,26 @@  static void mxt_input_close(struct input_dev *dev)
 		dev_err(&data->client->dev, "%s failed rc=%d\n", __func__, ret);
 }
 
+static ssize_t mxt_gpio_input_pin_read(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf)
+{
+	struct mxt_data *data = dev_get_drvdata(dev);
+	struct mxt_gpio_attr *attr_p = container_of(attr, struct mxt_gpio_attr,
+						    attr);
+	int pin_status = test_bit(attr_p->bit_index,
+				  &data->gpio_input_pin_status);
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", pin_status);
+}
+
 static int mxt_parse_device_properties(struct mxt_data *data)
 {
 	static const char keymap_property[] = "linux,gpio-keymap";
+	static const char gpios_property[] = "atmel,gpios";
 	struct device *dev = &data->client->dev;
+	struct device_node *np = dev ? dev->of_node : NULL;
+	struct device_node *np_gpio;
 	u32 *keymap;
 	int n_keys;
 	int error;
@@ -4036,7 +4072,89 @@  static int mxt_parse_device_properties(struct mxt_data *data)
 
 	device_property_read_u32(dev, "atmel,suspend-mode", &data->suspend_mode);
 
+	np_gpio = of_get_child_by_name(np, gpios_property);
+	if (np_gpio) {
+		int gpio_pin;
+		struct mxt_gpio_attr *attr_p;
+		char *sysfs_gpio_file_name;
+		u8 num_gpio_pins = 0;
+
+		np_gpio = of_find_node_with_property(np_gpio, "gpio");
+		if (!np_gpio)
+			return -EINVAL;
+
+		data->gpio_attrs.attrs =
+			devm_kzalloc(dev,
+				     9 * sizeof(struct attribute *),
+				     GFP_KERNEL);
+		if (!data->gpio_attrs.attrs) {
+			error = -ENOMEM;
+			goto err_gpios_property_put;
+		}
+
+		do {
+			attr_p = devm_kmalloc(dev,
+					      sizeof(struct mxt_gpio_attr),
+					      GFP_KERNEL);
+			if (!attr_p) {
+				error = -ENOMEM;
+				goto err_gpios_property_put;
+			}
+
+			error = of_property_read_u32_index(np_gpio, "gpio", 0,
+							   &gpio_pin);
+			if (error) {
+				dev_warn(dev,
+					 "Couldn't read gpio property for node : %s\n",
+					 np_gpio->name);
+				error = -EINVAL;
+				goto err_gpios_property_put;
+			}
+
+			if (gpio_pin > 7) {
+				dev_warn(dev,
+					 "Incorrect GPIO pin index for node %s: %u\n",
+					 np_gpio->name, gpio_pin);
+				error = -EINVAL;
+				goto err_gpios_property_put;
+			}
+
+			sysfs_gpio_file_name =
+					devm_kmalloc(dev,
+						     strlen(np_gpio->name) + 1,
+						     GFP_KERNEL);
+			if (!sysfs_gpio_file_name) {
+				error = -ENOMEM;
+				goto err_gpios_property_put;
+			}
+
+			strcpy(sysfs_gpio_file_name, np_gpio->name);
+
+			sysfs_attr_init(&attr_p->attr.attr);
+
+			attr_p->attr.attr.name = sysfs_gpio_file_name;
+			attr_p->attr.attr.mode = 0444;
+			attr_p->attr.show = mxt_gpio_input_pin_read;
+			attr_p->attr.store = NULL;
+			attr_p->bit_index = gpio_pin;
+
+			data->gpio_input_pin_status_default |= BIT(gpio_pin);
+
+			data->gpio_attrs.attrs[num_gpio_pins++] =
+							&attr_p->attr.attr;
+		} while ((np_gpio =
+			  of_find_node_with_property(np_gpio, "gpio")) &&
+			 num_gpio_pins < 8);
+
+		if (np_gpio)
+			of_node_put(np_gpio);
+	}
+
 	return 0;
+
+err_gpios_property_put:
+	of_node_put(np_gpio);
+	return error;
 }
 
 static const struct dmi_system_id chromebook_T9_suspend_dmi[] = {
@@ -4106,6 +4224,8 @@  static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
 	if (error)
 		return error;
 
+	data->gpio_input_pin_status = data->gpio_input_pin_status_default;
+
 	if (data->pcfg_name)
 		mxt_update_file_name(&data->client->dev,
 				     &data->cfg_name,