[PATCH v2 5/9] PM / ACPI: Provide option to disable direct_complete for ACPI devices
Ulf Hansson
ulf.hansson at linaro.org
Mon Aug 28 07:24:51 PDT 2017
On 28 August 2017 at 15:40, Rafael J. Wysocki <rjw at rjwysocki.net> wrote:
> On Monday, August 28, 2017 2:54:40 PM CEST Ulf Hansson wrote:
>> On 28 August 2017 at 14:39, Rafael J. Wysocki <rjw at rjwysocki.net> wrote:
>> > On Monday, August 28, 2017 10:31:44 AM CEST Ulf Hansson wrote:
>> >> On 28 August 2017 at 03:30, Rafael J. Wysocki <rjw at rjwysocki.net> wrote:
>> >> > On Friday, August 25, 2017 3:42:35 PM CEST Rafael J. Wysocki wrote:
>> >> >> On Thursday, August 24, 2017 11:50:40 PM CEST Rafael J. Wysocki wrote:
>> >> >> > On Thursday, August 24, 2017 6:35:49 PM CEST Rafael J. Wysocki wrote:
>> >> >> > > On Thursday, August 24, 2017 11:15:26 AM CEST Ulf Hansson wrote:
>
> [cut]
>
>> >> >
>> >> > Well, not really, because if the device remains runtime suspended,
>> >> > ->runtime_suspend() will not be called by pm_runtime_force_suspend() and
>> >> > acpi_dev_suspend_late() should not be called then.
>> >> >
>> >> > So more changes in the ACPI PM domain are needed after all.
>> >>
>> >> Yes, that's what I thought as well.
>> >>
>> >> Anyway, let me cook a new version of the series - trying to address
>> >> the first bits you have pointed out. Then we can continue with
>> >> fine-tuning on top, addressing further optimizations of the ACPI PM
>> >> domain.
>> >
>> > Actually, please hold on and let me show you what I would like to do
>> > first.
>>
>> Hmm.
>>
>> I think I have almost done the work for the ACPI PM domain already.
>> It's just a matter of minor tweaks to the changes in patch 6 and 7
>> (and of course to get them into a shape that you prefer) and then
>> dropping patch 5 altogether.
>>
>> Wouldn't it be better if you build upon my changes?
>>
>> Anyway, if you have strong opinion of driving this, I am fine stepping aside.
>
> OK, so see below. :-)
>
> If what you want is to make the i2c designware driver use _force_suspend() and
> _force_resume(), then to me this is only tangentially related to direct_complete
> and can be done without messing up with that one.
>
> So the problem is not when direct_complete is set (becasue the driver's and
> PM domain's callbacks will not be invoked then even), but when it is not set.
For i2c designware driver case - then you are right! Although, that's
because the i2c designware driver has nothing else to do than to call
pm_runtime_force_suspend|resume() from the late suspend and early
resume callbacks.
However, for other drivers this isn't the case. A driver may have some
additional things to cope with during system sleep, besides making
sure to call pm_runtime_force_suspend|resume().
Then as I stated earlier, it would be of great value if we could
remain having runtime PM enabled during the entire device_suspend()
phase. I am not sure how you intend to address that, or perhaps you
did in some of those earlier patches you posted.
In my re-spinned series (not posted yet), I am still addressing both
issues above, but also not preventing the direct_complete path for
parent/suppliers when the runtime PM centric path is used.
> If direct_complete is not set, the ACPI PM domain resumes the device in
> acpi_subsys_suspend(), because it doesn't know two things: (a) why direct_complete
> is not set and (b) whether or not the drivers PM callbacks can cope with a
> runtime suspended device. These two things are separate, so they need to
> be addressed separately.
Yes.
>
> For (b) I'd like to use the SAFE_SUSPEND flag from my previous patch.
Seems like a reasonable approach.
>
> As far as (a) is concerned, there are two possible reasons for not setting
> direct_complete. First, it may be necessary to change the power state of the
> device and in that case the device *should* be resumed in acpi_subsys_suspend().
> Second, direct_complete may not be set for the device's children and in that
> case acpi_subsys_suspend() may not care as long as SAFE_SUSPEND is set.
Okay!
>
> So the ACPI PM domain needs to distinguish these two cases and for that reason
> it has to track whether or not the power state of the device needs to be updated
> which is what the (untested) patch below does, but of course it doesn't
> cover the LPSS.
>
> ---
> Documentation/driver-api/pm/devices.rst | 7 ++
> drivers/acpi/device_pm.c | 72 +++++++++++++++++++++-------
> drivers/base/dd.c | 2
> drivers/i2c/busses/i2c-designware-platdrv.c | 4 +
> include/acpi/acpi_bus.h | 1
> include/linux/pm.h | 16 ++++++
> 6 files changed, 83 insertions(+), 19 deletions(-)
>
> Index: linux-pm/include/linux/pm.h
> ===================================================================
> --- linux-pm.orig/include/linux/pm.h
> +++ linux-pm/include/linux/pm.h
> @@ -550,6 +550,21 @@ struct pm_subsys_data {
> #endif
> };
>
> +/*
> + * Driver flags to control system suspend/resume behavior.
> + *
> + * These flags can be set by device drivers at the probe time. They need not be
> + * cleared by the drivers as the driver core will take care of that.
> + *
> + * SAFE_SUSPEND: No need to runtime resume the device during system suspend.
> + *
> + * Setting SAFE_SUSPEND instructs bus types and PM domains which may want to
> + * runtime resume the device upfront during system suspend that doing so is not
> + * necessary from the driver's perspective, because the system suspend callbacks
> + * provided by it can cope with a runtime suspended device.
> + */
> +#define DPM_FLAG_SAFE_SUSPEND BIT(0)
> +
> struct dev_pm_info {
> pm_message_t power_state;
> unsigned int can_wakeup:1;
> @@ -561,6 +576,7 @@ struct dev_pm_info {
> bool is_late_suspended:1;
> bool early_init:1; /* Owned by the PM core */
> bool direct_complete:1; /* Owned by the PM core */
> + unsigned int driver_flags;
> spinlock_t lock;
> #ifdef CONFIG_PM_SLEEP
> struct list_head entry;
> Index: linux-pm/drivers/acpi/device_pm.c
> ===================================================================
> --- linux-pm.orig/drivers/acpi/device_pm.c
> +++ linux-pm/drivers/acpi/device_pm.c
> @@ -899,6 +899,7 @@ int acpi_dev_runtime_resume(struct devic
>
> error = acpi_dev_pm_full_power(adev);
> acpi_device_wakeup_disable(adev);
> + adev->power.update_state = true;
> return error;
> }
> EXPORT_SYMBOL_GPL(acpi_dev_runtime_resume);
> @@ -989,33 +990,47 @@ int acpi_dev_resume_early(struct device
> }
> EXPORT_SYMBOL_GPL(acpi_dev_resume_early);
>
> -/**
> - * acpi_subsys_prepare - Prepare device for system transition to a sleep state.
> - * @dev: Device to prepare.
> - */
> -int acpi_subsys_prepare(struct device *dev)
> +static bool acpi_dev_state_update_needed(struct device *dev)
> {
> struct acpi_device *adev = ACPI_COMPANION(dev);
> u32 sys_target;
> int ret, state;
>
> - ret = pm_generic_prepare(dev);
> - if (ret < 0)
> - return ret;
> + if (!adev || !pm_runtime_suspended(dev))
> + return true;
>
> - if (!adev || !pm_runtime_suspended(dev)
> - || device_may_wakeup(dev) != !!adev->wakeup.prepare_count)
> - return 0;
> + if (device_may_wakeup(dev) != !!adev->wakeup.prepare_count)
> + return true;
>
> sys_target = acpi_target_system_state();
> if (sys_target == ACPI_STATE_S0)
> - return 1;
> + return false;
>
> if (adev->power.flags.dsw_present)
> - return 0;
> + return true;
>
> ret = acpi_dev_pm_get_state(dev, adev, sys_target, NULL, &state);
> - return !ret && state == adev->power.state;
> + if (ret)
> + return true;
> +
> + return state != adev->power.state;
> +}
> +
> +/**
> + * acpi_subsys_prepare - Prepare device for system transition to a sleep state.
> + * @dev: Device to prepare.
> + */
> +int acpi_subsys_prepare(struct device *dev)
> +{
> + struct acpi_device *adev = ACPI_COMPANION(dev);
> + int ret;
> +
> + ret = pm_generic_prepare(dev);
> + if (ret < 0)
> + return ret;
> +
> + adev->power.update_state = acpi_dev_state_update_needed(dev);
> + return !adev->power.update_state;
> }
> EXPORT_SYMBOL_GPL(acpi_subsys_prepare);
>
> @@ -1024,11 +1039,20 @@ EXPORT_SYMBOL_GPL(acpi_subsys_prepare);
> * @dev: Device to handle.
> *
> * Follow PCI and resume devices suspended at run time before running their
> - * system suspend callbacks.
> + * system suspend callbacks, unless the DPM_FLAG_SAFE_SUSPEND driver flag is
> + * set for them.
> */
> int acpi_subsys_suspend(struct device *dev)
> {
> - pm_runtime_resume(dev);
> + struct acpi_device *adev = ACPI_COMPANION(dev);
> +
> + if (!adev)
> + return 0;
> +
> + if (adev->power.update_state ||
> + !(dev->power.driver_flags & DPM_FLAG_SAFE_SUSPEND))
> + pm_runtime_resume(dev);
> +
> return pm_generic_suspend(dev);
> }
> EXPORT_SYMBOL_GPL(acpi_subsys_suspend);
> @@ -1042,8 +1066,20 @@ EXPORT_SYMBOL_GPL(acpi_subsys_suspend);
> */
> int acpi_subsys_suspend_late(struct device *dev)
> {
> - int ret = pm_generic_suspend_late(dev);
> - return ret ? ret : acpi_dev_suspend_late(dev);
> + struct acpi_device *adev = ACPI_COMPANION(dev);
> + int ret;
> +
> + if (!adev)
> + return 0;
> +
> + ret = pm_generic_suspend_late(dev);
> + if (ret)
> + return ret;
> +
> + if (adev->power.update_state)
> + return acpi_dev_suspend_late(dev);
> +
> + return 0;
> }
> EXPORT_SYMBOL_GPL(acpi_subsys_suspend_late);
>
> Index: linux-pm/drivers/base/dd.c
> ===================================================================
> --- linux-pm.orig/drivers/base/dd.c
> +++ linux-pm/drivers/base/dd.c
> @@ -436,6 +436,7 @@ pinctrl_bind_failed:
> if (dev->pm_domain && dev->pm_domain->dismiss)
> dev->pm_domain->dismiss(dev);
> pm_runtime_reinit(dev);
> + dev->power.driver_flags = 0;
>
> switch (ret) {
> case -EPROBE_DEFER:
> @@ -841,6 +842,7 @@ static void __device_release_driver(stru
> if (dev->pm_domain && dev->pm_domain->dismiss)
> dev->pm_domain->dismiss(dev);
> pm_runtime_reinit(dev);
> + dev->power.driver_flags = 0;
>
> klist_remove(&dev->p->knode_driver);
> device_pm_check_callbacks(dev);
> Index: linux-pm/Documentation/driver-api/pm/devices.rst
> ===================================================================
> --- linux-pm.orig/Documentation/driver-api/pm/devices.rst
> +++ linux-pm/Documentation/driver-api/pm/devices.rst
> @@ -729,6 +729,13 @@ state temporarily, for example so that i
> disabled. This all depends on the hardware and the design of the subsystem and
> device driver in question.
>
> +Some bus types and PM domains have a policy to runtime resume all
> +devices upfront in their ``->suspend`` callbacks, but that may not be really
> +necessary if the system suspend-resume callbacks provided by the device's
> +driver can cope with a runtime-suspended device. The driver can indicate that
> +by setting ``DPM_FLAG_SAFE_SUSPEND`` in :c:member:`power.driver_flags` at the
> +probe time.
> +
> During system-wide resume from a sleep state it's easiest to put devices into
> the full-power state, as explained in :file:`Documentation/power/runtime_pm.txt`.
> Refer to that document for more information regarding this particular issue as
> Index: linux-pm/include/acpi/acpi_bus.h
> ===================================================================
> --- linux-pm.orig/include/acpi/acpi_bus.h
> +++ linux-pm/include/acpi/acpi_bus.h
> @@ -287,6 +287,7 @@ struct acpi_device_power {
> int state; /* Current state */
> struct acpi_device_power_flags flags;
> struct acpi_device_power_state states[ACPI_D_STATE_COUNT]; /* Power states (D0-D3Cold) */
> + bool update_state;
> };
>
> /* Performance Management */
> Index: linux-pm/drivers/i2c/busses/i2c-designware-platdrv.c
> ===================================================================
> --- linux-pm.orig/drivers/i2c/busses/i2c-designware-platdrv.c
> +++ linux-pm/drivers/i2c/busses/i2c-designware-platdrv.c
> @@ -358,6 +358,7 @@ static int dw_i2c_plat_probe(struct plat
> if (dev->pm_disabled) {
> pm_runtime_forbid(&pdev->dev);
> } else {
> + dev->power.driver_flags = DPM_FLAG_SAFE_SUSPEND;
> pm_runtime_set_autosuspend_delay(&pdev->dev, 1000);
> pm_runtime_use_autosuspend(&pdev->dev);
> pm_runtime_set_active(&pdev->dev);
> @@ -455,7 +456,8 @@ static int dw_i2c_plat_resume(struct dev
> static const struct dev_pm_ops dw_i2c_dev_pm_ops = {
> .prepare = dw_i2c_plat_prepare,
> .complete = dw_i2c_plat_complete,
> - SET_SYSTEM_SLEEP_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume)
> + SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
> + pm_runtime_force_resume)
> SET_RUNTIME_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume, NULL)
> };
>
>
Kind regards
Uffe
More information about the linux-arm-kernel
mailing list