Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

i2c: designware: Fix unbalanced suspended flag

Ensure that i2c_mark_adapter_suspended() is always balanced by a call to
i2c_mark_adapter_resumed().

dw_i2c_plat_resume() must always be called, so that
i2c_mark_adapter_resumed() is called. This is not compatible with
DPM_FLAG_MAY_SKIP_RESUME, so remove the flag.

Since the controller is always resumed on system resume the
dw_i2c_plat_complete() callback is redundant and has been removed.

The unbalanced suspended flag was introduced by commit c57813b8b288
("i2c: designware: Lock the adapter while setting the suspended flag")

Before that commit, the system and runtime PM used the same functions. The
DPM_FLAG_MAY_SKIP_RESUME was used to skip the system resume if the driver
had been in runtime-suspend. If system resume was skipped, the suspended
flag would be cleared by the next runtime resume. The check of the
suspended flag was _after_ the call to pm_runtime_get_sync() in
i2c_dw_xfer(). So either a system resume or a runtime resume would clear
the flag before it was checked.

Having introduced the unbalanced suspended flag with that commit, a further
commit 80704a84a9f8
("i2c: designware: Use the i2c_mark_adapter_suspended/resumed() helpers")

changed from using a local suspended flag to using the
i2c_mark_adapter_suspended/resumed() functions. These use a flag that is
checked by I2C core code before issuing the transfer to the bus driver, so
there was no opportunity for the bus driver to runtime resume itself before
the flag check.

Signed-off-by: Richard Fitzgerald <rf@opensource.cirrus.com>
Fixes: c57813b8b288 ("i2c: designware: Lock the adapter while setting the suspended flag")
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Acked-by: Jarkko Nikula <jarkko.nikula@linux.intel.com>
Signed-off-by: Wolfram Sang <wsa@kernel.org>

authored by

Richard Fitzgerald and committed by
Wolfram Sang
75507a31 c8c37bc5

+2 -18
+2 -18
drivers/i2c/busses/i2c-designware-platdrv.c
··· 351 351 352 352 if (dev->flags & ACCESS_NO_IRQ_SUSPEND) { 353 353 dev_pm_set_driver_flags(&pdev->dev, 354 - DPM_FLAG_SMART_PREPARE | 355 - DPM_FLAG_MAY_SKIP_RESUME); 354 + DPM_FLAG_SMART_PREPARE); 356 355 } else { 357 356 dev_pm_set_driver_flags(&pdev->dev, 358 357 DPM_FLAG_SMART_PREPARE | 359 - DPM_FLAG_SMART_SUSPEND | 360 - DPM_FLAG_MAY_SKIP_RESUME); 358 + DPM_FLAG_SMART_SUSPEND); 361 359 } 362 360 363 361 device_enable_async_suspend(&pdev->dev); ··· 417 419 */ 418 420 return !has_acpi_companion(dev); 419 421 } 420 - 421 - static void dw_i2c_plat_complete(struct device *dev) 422 - { 423 - /* 424 - * The device can only be in runtime suspend at this point if it has not 425 - * been resumed throughout the ending system suspend/resume cycle, so if 426 - * the platform firmware might mess up with it, request the runtime PM 427 - * framework to resume it. 428 - */ 429 - if (pm_runtime_suspended(dev) && pm_resume_via_firmware()) 430 - pm_request_resume(dev); 431 - } 432 422 #else 433 423 #define dw_i2c_plat_prepare NULL 434 - #define dw_i2c_plat_complete NULL 435 424 #endif 436 425 437 426 #ifdef CONFIG_PM ··· 468 483 469 484 static const struct dev_pm_ops dw_i2c_dev_pm_ops = { 470 485 .prepare = dw_i2c_plat_prepare, 471 - .complete = dw_i2c_plat_complete, 472 486 SET_LATE_SYSTEM_SLEEP_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume) 473 487 SET_RUNTIME_PM_OPS(dw_i2c_plat_runtime_suspend, dw_i2c_plat_runtime_resume, NULL) 474 488 };