Blob Blame History Raw
From 71f52ec2f033907576dccf71ac1aec94348130e8 Mon Sep 17 00:00:00 2001
From: Oliver Neukum <oneukum@suse.com>
Date: Tue, 8 Jun 2021 10:31:29 +0200
Subject: [PATCH] NFC: SUSE specific brutal fix for runtime PM
Patch-mainline: Never, SUSE specific
References: bsc#1185589

We have an interaction between runtime PM on the
i2c level and the NFC layer. Specifically on some i2c
chipsets NFC readers will not work, because i2c's PM
will depower them while they need to be listening for connections.

The problem is specific to some i2c controllers and
all of NFC. The proper fix would be the add minimal
runtime PM to every NFC driver. That is not feasible
to us in a realistic time frame.

Hence this implementation does it on the NFC level,
which makes all systems work, at the expense of overriding
better implementations of specific drivers for i2c and USB.
But we support quite a number of drivers and they need to work.
So we are brutal.

Signed-off-by: Oliver Neukum <oneukum@suse.com>
---
 net/nfc/core.c | 20 +++++++++++++++++++-
 1 file changed, 19 insertions(+), 1 deletion(-)

diff --git a/net/nfc/core.c b/net/nfc/core.c
index c5f9c3ee8..1d6de505d 100644
--- a/net/nfc/core.c
+++ b/net/nfc/core.c
@@ -15,6 +15,7 @@
 #include <linux/slab.h>
 #include <linux/rfkill.h>
 #include <linux/nfc.h>
+#include <linux/pm_runtime.h>
 
 #include <net/genetlink.h>
 
@@ -37,6 +38,8 @@ int nfc_fw_download(struct nfc_dev *dev, const char *firmware_name)
 	pr_debug("%s do firmware %s\n", dev_name(&dev->dev), firmware_name);
 
 	device_lock(&dev->dev);
+	/* failures MUST be ignored */
+	pm_runtime_get_sync(&dev->dev);
 
 	if (!device_is_registered(&dev->dev)) {
 		rc = -ENODEV;
@@ -58,7 +61,11 @@ int nfc_fw_download(struct nfc_dev *dev, const char *firmware_name)
 	if (rc)
 		dev->fw_download_in_progress = false;
 
+	device_unlock(&dev->dev);
+	return 0;
+
 error:
+	pm_runtime_put(&dev->dev);
 	device_unlock(&dev->dev);
 	return rc;
 }
@@ -73,9 +80,12 @@ int nfc_fw_download(struct nfc_dev *dev, const char *firmware_name)
 int nfc_fw_download_done(struct nfc_dev *dev, const char *firmware_name,
 			 u32 result)
 {
+	int rc;
 	dev->fw_download_in_progress = false;
 
-	return nfc_genl_fw_download_done(dev, firmware_name, result);
+	rc = nfc_genl_fw_download_done(dev, firmware_name, result);
+	pm_runtime_put(&dev->dev);
+	return rc;
 }
 EXPORT_SYMBOL(nfc_fw_download_done);
 
@@ -93,6 +103,8 @@ int nfc_dev_up(struct nfc_dev *dev)
 	pr_debug("dev_name=%s\n", dev_name(&dev->dev));
 
 	device_lock(&dev->dev);
+	/* errors MUST be ignored, we are using the child count */
+	pm_runtime_get_sync(&dev->dev);
 
 	if (dev->rfkill && rfkill_blocked(dev->rfkill)) {
 		rc = -ERFKILL;
@@ -123,8 +135,11 @@ int nfc_dev_up(struct nfc_dev *dev)
 	/* We have to enable the device before discovering SEs */
 	if (dev->ops->discover_se && dev->ops->discover_se(dev))
 		pr_err("SE discovery failed\n");
+	device_unlock(&dev->dev);
+	return rc;
 
 error:
+	pm_runtime_put(&dev->dev);
 	device_unlock(&dev->dev);
 	return rc;
 }
@@ -161,6 +176,9 @@ int nfc_dev_down(struct nfc_dev *dev)
 		dev->ops->dev_down(dev);
 
 	dev->dev_up = false;
+	pm_runtime_put(&dev->dev);
+	device_unlock(&dev->dev);
+	return rc;
 
 error:
 	device_unlock(&dev->dev);
-- 
2.26.2