aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2018-01-02 19:37:34 -0500
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2018-01-09 18:48:25 -0500
commit02e45646d53bdb38bfb47b83765778d3ecb4d3b3 (patch)
tree44f34c5e89f46a22c9a112cb122f75e6cb96b7db
parent422cb781e0d0f81789a1cc0f2171611028450f09 (diff)
PM: i2c-designware-platdrv: Optimize power management
Optimize the power management in i2c-designware-platdrv by making it set the DPM_FLAG_SMART_SUSPEND and DPM_FLAG_LEAVE_SUSPENDED which allows some code to be dropped from its PM callbacks. First, setting DPM_FLAG_SMART_SUSPEND causes the intel-lpss driver to avoid resuming i2c-designware-platdrv devices in its ->prepare callback, so they can stay in runtime suspend after that point even if the direct-complete feature is not used for them. It also causes the ACPI PM domain and the PM core to avoid invoking "late" and "noirq" suspend callbacks for these devices if they are in runtime suspend at the beginning of the "late" phase of device suspend during system suspend. That guarantees dw_i2c_plat_suspend() to be called for a device only if it is not in runtime suspend. Moreover, it causes the device's runtime PM status to be set to "active" after calling dw_i2c_plat_resume() for it, so the driver doesn't need internal flags to avoid invoking either dw_i2c_plat_suspend() or dw_i2c_plat_resume() twice in a row. Second, setting DPM_FLAG_LEAVE_SUSPENDED enables the optimization allowing the device to stay suspended after system resume under suitable conditions, so again the driver doesn't need to take care of that by itself. Accordingly, the internal "suspended" and "skip_resume" flags used by the driver are not necessary any more, so drop them and simplify the driver's PM callbacks. Additionally, notice that dw_i2c_plat_complete() only needs to schedule runtime PM resume for the device if platform firmware has been involved in resuming the system, so make it call pm_resume_via_firmware() to check that. Also make it check the runtime PM status of the device instead of its direct_complete flag which also works if the device remained suspended due to the DPM_FLAG_LEAVE_SUSPENDED driver flag. Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Acked-by: Jarkko Nikula <jarkko.nikula@linux.intel.com> Acked-by: Wolfram Sang <wsa@the-dreams.de> Tested-by: Jarkko Nikula <jarkko.nikula@linux.intel.com>
-rw-r--r--drivers/i2c/busses/i2c-designware-core.h2
-rw-r--r--drivers/i2c/busses/i2c-designware-platdrv.c31
2 files changed, 12 insertions, 21 deletions
diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h
index 21bf619a86c5..9fee4c054d3d 100644
--- a/drivers/i2c/busses/i2c-designware-core.h
+++ b/drivers/i2c/busses/i2c-designware-core.h
@@ -280,8 +280,6 @@ struct dw_i2c_dev {
280 int (*acquire_lock)(struct dw_i2c_dev *dev); 280 int (*acquire_lock)(struct dw_i2c_dev *dev);
281 void (*release_lock)(struct dw_i2c_dev *dev); 281 void (*release_lock)(struct dw_i2c_dev *dev);
282 bool pm_disabled; 282 bool pm_disabled;
283 bool suspended;
284 bool skip_resume;
285 void (*disable)(struct dw_i2c_dev *dev); 283 void (*disable)(struct dw_i2c_dev *dev);
286 void (*disable_int)(struct dw_i2c_dev *dev); 284 void (*disable_int)(struct dw_i2c_dev *dev);
287 int (*init)(struct dw_i2c_dev *dev); 285 int (*init)(struct dw_i2c_dev *dev);
diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c
index 4f90a6dc186f..153b947702c5 100644
--- a/drivers/i2c/busses/i2c-designware-platdrv.c
+++ b/drivers/i2c/busses/i2c-designware-platdrv.c
@@ -42,6 +42,7 @@
42#include <linux/reset.h> 42#include <linux/reset.h>
43#include <linux/sched.h> 43#include <linux/sched.h>
44#include <linux/slab.h> 44#include <linux/slab.h>
45#include <linux/suspend.h>
45 46
46#include "i2c-designware-core.h" 47#include "i2c-designware-core.h"
47 48
@@ -372,7 +373,10 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
372 ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev)); 373 ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev));
373 adap->dev.of_node = pdev->dev.of_node; 374 adap->dev.of_node = pdev->dev.of_node;
374 375
375 dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_SMART_PREPARE); 376 dev_pm_set_driver_flags(&pdev->dev,
377 DPM_FLAG_SMART_PREPARE |
378 DPM_FLAG_SMART_SUSPEND |
379 DPM_FLAG_LEAVE_SUSPENDED);
376 380
377 /* The code below assumes runtime PM to be disabled. */ 381 /* The code below assumes runtime PM to be disabled. */
378 WARN_ON(pm_runtime_enabled(&pdev->dev)); 382 WARN_ON(pm_runtime_enabled(&pdev->dev));
@@ -448,7 +452,13 @@ static int dw_i2c_plat_prepare(struct device *dev)
448 452
449static void dw_i2c_plat_complete(struct device *dev) 453static void dw_i2c_plat_complete(struct device *dev)
450{ 454{
451 if (dev->power.direct_complete) 455 /*
456 * The device can only be in runtime suspend at this point if it has not
457 * been resumed throughout the ending system suspend/resume cycle, so if
458 * the platform firmware might mess up with it, request the runtime PM
459 * framework to resume it.
460 */
461 if (pm_runtime_suspended(dev) && pm_resume_via_firmware())
452 pm_request_resume(dev); 462 pm_request_resume(dev);
453} 463}
454#else 464#else
@@ -461,16 +471,9 @@ static int dw_i2c_plat_suspend(struct device *dev)
461{ 471{
462 struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); 472 struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
463 473
464 if (i_dev->suspended) {
465 i_dev->skip_resume = true;
466 return 0;
467 }
468
469 i_dev->disable(i_dev); 474 i_dev->disable(i_dev);
470 i2c_dw_plat_prepare_clk(i_dev, false); 475 i2c_dw_plat_prepare_clk(i_dev, false);
471 476
472 i_dev->suspended = true;
473
474 return 0; 477 return 0;
475} 478}
476 479
@@ -478,19 +481,9 @@ static int dw_i2c_plat_resume(struct device *dev)
478{ 481{
479 struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); 482 struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
480 483
481 if (!i_dev->suspended)
482 return 0;
483
484 if (i_dev->skip_resume) {
485 i_dev->skip_resume = false;
486 return 0;
487 }
488
489 i2c_dw_plat_prepare_clk(i_dev, true); 484 i2c_dw_plat_prepare_clk(i_dev, true);
490 i_dev->init(i_dev); 485 i_dev->init(i_dev);
491 486
492 i_dev->suspended = false;
493
494 return 0; 487 return 0;
495} 488}
496 489