Merge tag 'mtd/for-4.16' of git://git.infradead.org/linux-mtd
[linux-2.6-microblaze.git] / drivers / i2c / busses / i2c-designware-platdrv.c
index 58add69..153b947 100644 (file)
@@ -42,6 +42,7 @@
 #include <linux/reset.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
+#include <linux/suspend.h>
 
 #include "i2c-designware-core.h"
 
@@ -372,6 +373,11 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
        ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev));
        adap->dev.of_node = pdev->dev.of_node;
 
+       dev_pm_set_driver_flags(&pdev->dev,
+                               DPM_FLAG_SMART_PREPARE |
+                               DPM_FLAG_SMART_SUSPEND |
+                               DPM_FLAG_LEAVE_SUSPENDED);
+
        /* The code below assumes runtime PM to be disabled. */
        WARN_ON(pm_runtime_enabled(&pdev->dev));
 
@@ -435,12 +441,24 @@ MODULE_DEVICE_TABLE(of, dw_i2c_of_match);
 #ifdef CONFIG_PM_SLEEP
 static int dw_i2c_plat_prepare(struct device *dev)
 {
-       return pm_runtime_suspended(dev);
+       /*
+        * If the ACPI companion device object is present for this device, it
+        * may be accessed during suspend and resume of other devices via I2C
+        * operation regions, so tell the PM core and middle layers to avoid
+        * skipping system suspend/resume callbacks for it in that case.
+        */
+       return !has_acpi_companion(dev);
 }
 
 static void dw_i2c_plat_complete(struct device *dev)
 {
-       if (dev->power.direct_complete)
+       /*
+        * The device can only be in runtime suspend at this point if it has not
+        * been resumed throughout the ending system suspend/resume cycle, so if
+        * the platform firmware might mess up with it, request the runtime PM
+        * framework to resume it.
+        */
+       if (pm_runtime_suspended(dev) && pm_resume_via_firmware())
                pm_request_resume(dev);
 }
 #else
@@ -453,16 +471,9 @@ static int dw_i2c_plat_suspend(struct device *dev)
 {
        struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
 
-       if (i_dev->suspended) {
-               i_dev->skip_resume = true;
-               return 0;
-       }
-
        i_dev->disable(i_dev);
        i2c_dw_plat_prepare_clk(i_dev, false);
 
-       i_dev->suspended = true;
-
        return 0;
 }
 
@@ -470,19 +481,9 @@ static int dw_i2c_plat_resume(struct device *dev)
 {
        struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
 
-       if (!i_dev->suspended)
-               return 0;
-
-       if (i_dev->skip_resume) {
-               i_dev->skip_resume = false;
-               return 0;
-       }
-
        i2c_dw_plat_prepare_clk(i_dev, true);
        i_dev->init(i_dev);
 
-       i_dev->suspended = false;
-
        return 0;
 }