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