Pablo Greco 7b2c62
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
Pablo Greco 7b2c62
From: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Pablo Greco 7b2c62
Date: Mon, 3 Apr 2017 18:18:21 +0200
Pablo Greco 7b2c62
Subject: [PATCH] Input: rmi4 - remove the need for artificial IRQ in case of
Pablo Greco 7b2c62
 HID
Pablo Greco 7b2c62
Pablo Greco 7b2c62
The IRQ from rmi4 may interfere with the one we currently use on i2c-hid.
Pablo Greco 7b2c62
Given that there is already a need for an external API from rmi4 to
Pablo Greco 7b2c62
forward the attention data, we can, in this particular case rely on a
Pablo Greco 7b2c62
separate workqueue to prevent cursor jumps.
Pablo Greco 7b2c62
Pablo Greco 7b2c62
Reported-by: Cameron Gutman <aicommander@gmail.com>
Pablo Greco 7b2c62
Reported-by: Thorsten Leemhuis <linux@leemhuis.info>
Pablo Greco 7b2c62
Reported-by: Jason Ekstrand <jason@jlekstrand.net>
Pablo Greco 7b2c62
Tested-by: Andrew Duggan <aduggan@synaptics.com>
Pablo Greco 7b2c62
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Pablo Greco 7b2c62
Signed-off-by: Lyude <lyude@redhat.com>
Pablo Greco 7b2c62
---
Pablo Greco 7b2c62
 drivers/hid/hid-rmi.c           |  64 -----------------
Pablo Greco 7b2c62
 drivers/input/rmi4/rmi_driver.c | 124 +++++++++++++++++++-------------
Pablo Greco 7b2c62
 include/linux/rmi.h             |   1 +
Pablo Greco 7b2c62
 3 files changed, 75 insertions(+), 114 deletions(-)
Pablo Greco 7b2c62
Pablo Greco 7b2c62
diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c
Pablo Greco 7b2c62
index 8cffa84c9650..6c4e3675601a 100644
Pablo Greco 7b2c62
--- a/drivers/hid/hid-rmi.c
Pablo Greco 7b2c62
+++ b/drivers/hid/hid-rmi.c
Pablo Greco 7b2c62
@@ -322,19 +322,12 @@ static int rmi_input_event(struct hid_device *hdev, u8 *data, int size)
Pablo Greco 7b2c62
 {
Pablo Greco 7b2c62
 	struct rmi_data *hdata = hid_get_drvdata(hdev);
Pablo Greco 7b2c62
 	struct rmi_device *rmi_dev = hdata->xport.rmi_dev;
Pablo Greco 7b2c62
-	unsigned long flags;
Pablo Greco 7b2c62
Pablo Greco 7b2c62
 	if (!(test_bit(RMI_STARTED, &hdata->flags)))
Pablo Greco 7b2c62
 		return 0;
Pablo Greco 7b2c62
Pablo Greco 7b2c62
-	local_irq_save(flags);
Pablo Greco 7b2c62
-
Pablo Greco 7b2c62
 	rmi_set_attn_data(rmi_dev, data[1], &data[2], size - 2);
Pablo Greco 7b2c62
Pablo Greco 7b2c62
-	generic_handle_irq(hdata->rmi_irq);
Pablo Greco 7b2c62
-
Pablo Greco 7b2c62
-	local_irq_restore(flags);
Pablo Greco 7b2c62
-
Pablo Greco 7b2c62
 	return 1;
Pablo Greco 7b2c62
 }
Pablo Greco 7b2c62
Pablo Greco 7b2c62
@@ -592,56 +585,6 @@ static const struct rmi_transport_ops hid_rmi_ops = {
Pablo Greco 7b2c62
 	.reset		= rmi_hid_reset,
Pablo Greco 7b2c62
 };
Pablo Greco 7b2c62
Pablo Greco 7b2c62
-static void rmi_irq_teardown(void *data)
Pablo Greco 7b2c62
-{
Pablo Greco 7b2c62
-	struct rmi_data *hdata = data;
Pablo Greco 7b2c62
-	struct irq_domain *domain = hdata->domain;
Pablo Greco 7b2c62
-
Pablo Greco 7b2c62
-	if (!domain)
Pablo Greco 7b2c62
-		return;
Pablo Greco 7b2c62
-
Pablo Greco 7b2c62
-	irq_dispose_mapping(irq_find_mapping(domain, 0));
Pablo Greco 7b2c62
-
Pablo Greco 7b2c62
-	irq_domain_remove(domain);
Pablo Greco 7b2c62
-	hdata->domain = NULL;
Pablo Greco 7b2c62
-	hdata->rmi_irq = 0;
Pablo Greco 7b2c62
-}
Pablo Greco 7b2c62
-
Pablo Greco 7b2c62
-static int rmi_irq_map(struct irq_domain *h, unsigned int virq,
Pablo Greco 7b2c62
-		       irq_hw_number_t hw_irq_num)
Pablo Greco 7b2c62
-{
Pablo Greco 7b2c62
-	irq_set_chip_and_handler(virq, &dummy_irq_chip, handle_simple_irq);
Pablo Greco 7b2c62
-
Pablo Greco 7b2c62
-	return 0;
Pablo Greco 7b2c62
-}
Pablo Greco 7b2c62
-
Pablo Greco 7b2c62
-static const struct irq_domain_ops rmi_irq_ops = {
Pablo Greco 7b2c62
-	.map = rmi_irq_map,
Pablo Greco 7b2c62
-};
Pablo Greco 7b2c62
-
Pablo Greco 7b2c62
-static int rmi_setup_irq_domain(struct hid_device *hdev)
Pablo Greco 7b2c62
-{
Pablo Greco 7b2c62
-	struct rmi_data *hdata = hid_get_drvdata(hdev);
Pablo Greco 7b2c62
-	int ret;
Pablo Greco 7b2c62
-
Pablo Greco 7b2c62
-	hdata->domain = irq_domain_create_linear(hdev->dev.fwnode, 1,
Pablo Greco 7b2c62
-						 &rmi_irq_ops, hdata);
Pablo Greco 7b2c62
-	if (!hdata->domain)
Pablo Greco 7b2c62
-		return -ENOMEM;
Pablo Greco 7b2c62
-
Pablo Greco 7b2c62
-	ret = devm_add_action_or_reset(&hdev->dev, &rmi_irq_teardown, hdata);
Pablo Greco 7b2c62
-	if (ret)
Pablo Greco 7b2c62
-		return ret;
Pablo Greco 7b2c62
-
Pablo Greco 7b2c62
-	hdata->rmi_irq = irq_create_mapping(hdata->domain, 0);
Pablo Greco 7b2c62
-	if (hdata->rmi_irq <= 0) {
Pablo Greco 7b2c62
-		hid_err(hdev, "Can't allocate an IRQ\n");
Pablo Greco 7b2c62
-		return hdata->rmi_irq < 0 ? hdata->rmi_irq : -ENXIO;
Pablo Greco 7b2c62
-	}
Pablo Greco 7b2c62
-
Pablo Greco 7b2c62
-	return 0;
Pablo Greco 7b2c62
-}
Pablo Greco 7b2c62
-
Pablo Greco 7b2c62
 static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id)
Pablo Greco 7b2c62
 {
Pablo Greco 7b2c62
 	struct rmi_data *data = NULL;
Pablo Greco 7b2c62
@@ -714,18 +657,11 @@ static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id)
Pablo Greco 7b2c62
Pablo Greco 7b2c62
 	mutex_init(&data->page_mutex);
Pablo Greco 7b2c62
Pablo Greco 7b2c62
-	ret = rmi_setup_irq_domain(hdev);
Pablo Greco 7b2c62
-	if (ret) {
Pablo Greco 7b2c62
-		hid_err(hdev, "failed to allocate IRQ domain\n");
Pablo Greco 7b2c62
-		return ret;
Pablo Greco 7b2c62
-	}
Pablo Greco 7b2c62
-
Pablo Greco 7b2c62
 	if (data->device_flags & RMI_DEVICE_HAS_PHYS_BUTTONS)
Pablo Greco 7b2c62
 		rmi_hid_pdata.gpio_data.disable = true;
Pablo Greco 7b2c62
Pablo Greco 7b2c62
 	data->xport.dev = hdev->dev.parent;
Pablo Greco 7b2c62
 	data->xport.pdata = rmi_hid_pdata;
Pablo Greco 7b2c62
-	data->xport.pdata.irq = data->rmi_irq;
Pablo Greco 7b2c62
 	data->xport.proto_name = "hid";
Pablo Greco 7b2c62
 	data->xport.ops = &hid_rmi_ops;
Pablo Greco 7b2c62
Pablo Greco 7b2c62
diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
Pablo Greco 7b2c62
index 258d5fe3d395..f7298e3dc8f3 100644
Pablo Greco 7b2c62
--- a/drivers/input/rmi4/rmi_driver.c
Pablo Greco 7b2c62
+++ b/drivers/input/rmi4/rmi_driver.c
Pablo Greco 7b2c62
@@ -182,34 +182,47 @@ void rmi_set_attn_data(struct rmi_device *rmi_dev, unsigned long irq_status,
Pablo Greco 7b2c62
 	attn_data.data = fifo_data;
Pablo Greco 7b2c62
Pablo Greco 7b2c62
 	kfifo_put(&drvdata->attn_fifo, attn_data);
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	schedule_work(&drvdata->attn_work);
Pablo Greco 7b2c62
 }
Pablo Greco 7b2c62
 EXPORT_SYMBOL_GPL(rmi_set_attn_data);
Pablo Greco 7b2c62
Pablo Greco 7b2c62
-static irqreturn_t rmi_irq_fn(int irq, void *dev_id)
Pablo Greco 7b2c62
+static void attn_callback(struct work_struct *work)
Pablo Greco 7b2c62
 {
Pablo Greco 7b2c62
-	struct rmi_device *rmi_dev = dev_id;
Pablo Greco 7b2c62
-	struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev);
Pablo Greco 7b2c62
+	struct rmi_driver_data *drvdata = container_of(work,
Pablo Greco 7b2c62
+							struct rmi_driver_data,
Pablo Greco 7b2c62
+							attn_work);
Pablo Greco 7b2c62
 	struct rmi4_attn_data attn_data = {0};
Pablo Greco 7b2c62
 	int ret, count;
Pablo Greco 7b2c62
Pablo Greco 7b2c62
 	count = kfifo_get(&drvdata->attn_fifo, &attn_data);
Pablo Greco 7b2c62
-	if (count) {
Pablo Greco 7b2c62
-		*(drvdata->irq_status) = attn_data.irq_status;
Pablo Greco 7b2c62
-		drvdata->attn_data = attn_data;
Pablo Greco 7b2c62
-	}
Pablo Greco 7b2c62
+	if (!count)
Pablo Greco 7b2c62
+		return;
Pablo Greco 7b2c62
Pablo Greco 7b2c62
-	ret = rmi_process_interrupt_requests(rmi_dev);
Pablo Greco 7b2c62
+	*(drvdata->irq_status) = attn_data.irq_status;
Pablo Greco 7b2c62
+	drvdata->attn_data = attn_data;
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	ret = rmi_process_interrupt_requests(drvdata->rmi_dev);
Pablo Greco 7b2c62
 	if (ret)
Pablo Greco 7b2c62
-		rmi_dbg(RMI_DEBUG_CORE, &rmi_dev->dev,
Pablo Greco 7b2c62
+		rmi_dbg(RMI_DEBUG_CORE, &drvdata->rmi_dev->dev,
Pablo Greco 7b2c62
 			"Failed to process interrupt request: %d\n", ret);
Pablo Greco 7b2c62
Pablo Greco 7b2c62
-	if (count) {
Pablo Greco 7b2c62
-		kfree(attn_data.data);
Pablo Greco 7b2c62
-		drvdata->attn_data.data = NULL;
Pablo Greco 7b2c62
-	}
Pablo Greco 7b2c62
+	kfree(attn_data.data);
Pablo Greco 7b2c62
+	drvdata->attn_data.data = NULL;
Pablo Greco 7b2c62
Pablo Greco 7b2c62
 	if (!kfifo_is_empty(&drvdata->attn_fifo))
Pablo Greco 7b2c62
-		return rmi_irq_fn(irq, dev_id);
Pablo Greco 7b2c62
+		schedule_work(&drvdata->attn_work);
Pablo Greco 7b2c62
+}
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+static irqreturn_t rmi_irq_fn(int irq, void *dev_id)
Pablo Greco 7b2c62
+{
Pablo Greco 7b2c62
+	struct rmi_device *rmi_dev = dev_id;
Pablo Greco 7b2c62
+	int ret;
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	ret = rmi_process_interrupt_requests(rmi_dev);
Pablo Greco 7b2c62
+	if (ret)
Pablo Greco 7b2c62
+		rmi_dbg(RMI_DEBUG_CORE, &rmi_dev->dev,
Pablo Greco 7b2c62
+			"Failed to process interrupt request: %d\n", ret);
Pablo Greco 7b2c62
Pablo Greco 7b2c62
 	return IRQ_HANDLED;
Pablo Greco 7b2c62
 }
Pablo Greco 7b2c62
@@ -217,7 +230,6 @@ static irqreturn_t rmi_irq_fn(int irq, void *dev_id)
Pablo Greco 7b2c62
 static int rmi_irq_init(struct rmi_device *rmi_dev)
Pablo Greco 7b2c62
 {
Pablo Greco 7b2c62
 	struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev);
Pablo Greco 7b2c62
-	struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
Pablo Greco 7b2c62
 	int irq_flags = irq_get_trigger_type(pdata->irq);
Pablo Greco 7b2c62
 	int ret;
Pablo Greco 7b2c62
Pablo Greco 7b2c62
@@ -235,8 +247,6 @@ static int rmi_irq_init(struct rmi_device *rmi_dev)
Pablo Greco 7b2c62
 		return ret;
Pablo Greco 7b2c62
 	}
Pablo Greco 7b2c62
Pablo Greco 7b2c62
-	data->enabled = true;
Pablo Greco 7b2c62
-
Pablo Greco 7b2c62
 	return 0;
Pablo Greco 7b2c62
 }
Pablo Greco 7b2c62
Pablo Greco 7b2c62
@@ -886,23 +896,27 @@ void rmi_enable_irq(struct rmi_device *rmi_dev, bool clear_wake)
Pablo Greco 7b2c62
 	if (data->enabled)
Pablo Greco 7b2c62
 		goto out;
Pablo Greco 7b2c62
Pablo Greco 7b2c62
-	enable_irq(irq);
Pablo Greco 7b2c62
-	data->enabled = true;
Pablo Greco 7b2c62
-	if (clear_wake && device_may_wakeup(rmi_dev->xport->dev)) {
Pablo Greco 7b2c62
-		retval = disable_irq_wake(irq);
Pablo Greco 7b2c62
-		if (retval)
Pablo Greco 7b2c62
-			dev_warn(&rmi_dev->dev,
Pablo Greco 7b2c62
-				 "Failed to disable irq for wake: %d\n",
Pablo Greco 7b2c62
-				 retval);
Pablo Greco 7b2c62
-	}
Pablo Greco 7b2c62
+	if (irq) {
Pablo Greco 7b2c62
+		enable_irq(irq);
Pablo Greco 7b2c62
+		data->enabled = true;
Pablo Greco 7b2c62
+		if (clear_wake && device_may_wakeup(rmi_dev->xport->dev)) {
Pablo Greco 7b2c62
+			retval = disable_irq_wake(irq);
Pablo Greco 7b2c62
+			if (retval)
Pablo Greco 7b2c62
+				dev_warn(&rmi_dev->dev,
Pablo Greco 7b2c62
+					 "Failed to disable irq for wake: %d\n",
Pablo Greco 7b2c62
+					 retval);
Pablo Greco 7b2c62
+		}
Pablo Greco 7b2c62
Pablo Greco 7b2c62
-	/*
Pablo Greco 7b2c62
-	 * Call rmi_process_interrupt_requests() after enabling irq,
Pablo Greco 7b2c62
-	 * otherwise we may lose interrupt on edge-triggered systems.
Pablo Greco 7b2c62
-	 */
Pablo Greco 7b2c62
-	irq_flags = irq_get_trigger_type(pdata->irq);
Pablo Greco 7b2c62
-	if (irq_flags & IRQ_TYPE_EDGE_BOTH)
Pablo Greco 7b2c62
-		rmi_process_interrupt_requests(rmi_dev);
Pablo Greco 7b2c62
+		/*
Pablo Greco 7b2c62
+		 * Call rmi_process_interrupt_requests() after enabling irq,
Pablo Greco 7b2c62
+		 * otherwise we may lose interrupt on edge-triggered systems.
Pablo Greco 7b2c62
+		 */
Pablo Greco 7b2c62
+		irq_flags = irq_get_trigger_type(pdata->irq);
Pablo Greco 7b2c62
+		if (irq_flags & IRQ_TYPE_EDGE_BOTH)
Pablo Greco 7b2c62
+			rmi_process_interrupt_requests(rmi_dev);
Pablo Greco 7b2c62
+	} else {
Pablo Greco 7b2c62
+		data->enabled = true;
Pablo Greco 7b2c62
+	}
Pablo Greco 7b2c62
Pablo Greco 7b2c62
 out:
Pablo Greco 7b2c62
 	mutex_unlock(&data->enabled_mutex);
Pablo Greco 7b2c62
@@ -922,20 +936,22 @@ void rmi_disable_irq(struct rmi_device *rmi_dev, bool enable_wake)
Pablo Greco 7b2c62
 		goto out;
Pablo Greco 7b2c62
Pablo Greco 7b2c62
 	data->enabled = false;
Pablo Greco 7b2c62
-	disable_irq(irq);
Pablo Greco 7b2c62
-	if (enable_wake && device_may_wakeup(rmi_dev->xport->dev)) {
Pablo Greco 7b2c62
-		retval = enable_irq_wake(irq);
Pablo Greco 7b2c62
-		if (retval)
Pablo Greco 7b2c62
-			dev_warn(&rmi_dev->dev,
Pablo Greco 7b2c62
-				 "Failed to enable irq for wake: %d\n",
Pablo Greco 7b2c62
-				 retval);
Pablo Greco 7b2c62
-	}
Pablo Greco 7b2c62
-
Pablo Greco 7b2c62
-	/* make sure the fifo is clean */
Pablo Greco 7b2c62
-	while (!kfifo_is_empty(&data->attn_fifo)) {
Pablo Greco 7b2c62
-		count = kfifo_get(&data->attn_fifo, &attn_data);
Pablo Greco 7b2c62
-		if (count)
Pablo Greco 7b2c62
-			kfree(attn_data.data);
Pablo Greco 7b2c62
+	if (irq) {
Pablo Greco 7b2c62
+		disable_irq(irq);
Pablo Greco 7b2c62
+		if (enable_wake && device_may_wakeup(rmi_dev->xport->dev)) {
Pablo Greco 7b2c62
+			retval = enable_irq_wake(irq);
Pablo Greco 7b2c62
+			if (retval)
Pablo Greco 7b2c62
+				dev_warn(&rmi_dev->dev,
Pablo Greco 7b2c62
+					 "Failed to enable irq for wake: %d\n",
Pablo Greco 7b2c62
+					 retval);
Pablo Greco 7b2c62
+		}
Pablo Greco 7b2c62
+	} else {
Pablo Greco 7b2c62
+		/* make sure the fifo is clean */
Pablo Greco 7b2c62
+		while (!kfifo_is_empty(&data->attn_fifo)) {
Pablo Greco 7b2c62
+			count = kfifo_get(&data->attn_fifo, &attn_data);
Pablo Greco 7b2c62
+			if (count)
Pablo Greco 7b2c62
+				kfree(attn_data.data);
Pablo Greco 7b2c62
+		}
Pablo Greco 7b2c62
 	}
Pablo Greco 7b2c62
Pablo Greco 7b2c62
 out:
Pablo Greco 7b2c62
@@ -981,6 +997,8 @@ static int rmi_driver_remove(struct device *dev)
Pablo Greco 7b2c62
 	irq_domain_remove(data->irqdomain);
Pablo Greco 7b2c62
 	data->irqdomain = NULL;
Pablo Greco 7b2c62
Pablo Greco 7b2c62
+	cancel_work_sync(&data->attn_work);
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
 	rmi_f34_remove_sysfs(rmi_dev);
Pablo Greco 7b2c62
 	rmi_free_function_list(rmi_dev);
Pablo Greco 7b2c62
Pablo Greco 7b2c62
@@ -1219,9 +1237,15 @@ static int rmi_driver_probe(struct device *dev)
Pablo Greco 7b2c62
 		}
Pablo Greco 7b2c62
 	}
Pablo Greco 7b2c62
Pablo Greco 7b2c62
-	retval = rmi_irq_init(rmi_dev);
Pablo Greco 7b2c62
-	if (retval < 0)
Pablo Greco 7b2c62
-		goto err_destroy_functions;
Pablo Greco 7b2c62
+	if (pdata->irq) {
Pablo Greco 7b2c62
+		retval = rmi_irq_init(rmi_dev);
Pablo Greco 7b2c62
+		if (retval < 0)
Pablo Greco 7b2c62
+			goto err_destroy_functions;
Pablo Greco 7b2c62
+	}
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	data->enabled = true;
Pablo Greco 7b2c62
+
Pablo Greco 7b2c62
+	INIT_WORK(&data->attn_work, attn_callback);
Pablo Greco 7b2c62
Pablo Greco 7b2c62
 	if (data->f01_container->dev.driver) {
Pablo Greco 7b2c62
 		/* Driver already bound, so enable ATTN now. */
Pablo Greco 7b2c62
diff --git a/include/linux/rmi.h b/include/linux/rmi.h
Pablo Greco 7b2c62
index 8ed37f93f3c8..d7ad35a15acb 100644
Pablo Greco 7b2c62
--- a/include/linux/rmi.h
Pablo Greco 7b2c62
+++ b/include/linux/rmi.h
Pablo Greco 7b2c62
@@ -363,6 +363,7 @@ struct rmi_driver_data {
Pablo Greco 7b2c62
Pablo Greco 7b2c62
 	struct rmi4_attn_data attn_data;
Pablo Greco 7b2c62
 	DECLARE_KFIFO(attn_fifo, struct rmi4_attn_data, 16);
Pablo Greco 7b2c62
+	struct work_struct attn_work;
Pablo Greco 7b2c62
 };
Pablo Greco 7b2c62
Pablo Greco 7b2c62
 int rmi_register_transport_device(struct rmi_transport_dev *xport);
Pablo Greco 7b2c62
-- 
Pablo Greco 7b2c62
2.28.0
Pablo Greco 7b2c62