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