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