Skip to content

Commit

Permalink
Merge tag 'pm-5.16-rc1-2' of git://git.kernel.org/pub/scm/linux/kerne…
Browse files Browse the repository at this point in the history
…l/git/rafael/linux-pm

Pull more power management updates from Rafael Wysocki:
 "These fix three intel_pstate driver regressions, fix locking in the
  core code suspending and resuming devices during system PM
  transitions, fix the handling of cpuidle drivers based on runtime PM
  during system-wide suspend, fix two issues in the operating
  performance points (OPP) framework and resource-managed helpers to it.

  Specifics:

   - Fix two intel_pstate driver regressions related to the HWP
     interrupt handling added recently (Srinivas Pandruvada).

   - Fix intel_pstate driver regression introduced during the 5.11 cycle
     and causing HWP desired performance to be mishandled in some cases
     when switching driver modes and during system suspend and shutdown
     (Rafael Wysocki).

   - Fix system-wide device suspend and resume locking to avoid
     deadlocks when device objects are deleted during a system-wide PM
     transition (Rafael Wysocki).

   - Modify system-wide suspend of devices to prevent cpuidle drivers
     based on runtime PM from misbehaving during the "no IRQ" phase of
     it (Ulf Hansson).

   - Fix return value of _opp_add_static_v2() helper (YueHaibing).

   - Fix required-opp handle count (Pavankumar Kondeti).

   - Add resource managed OPP helpers, update dev_pm_opp_attach_genpd(),
     update their devfreq users, and make minor DT binding change
     (Dmitry Osipenko)"

* tag 'pm-5.16-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm:
  PM: sleep: Avoid calling put_device() under dpm_list_mtx
  cpufreq: intel_pstate: Clear HWP Status during HWP Interrupt enable
  cpufreq: intel_pstate: Fix unchecked MSR 0x773 access
  cpufreq: intel_pstate: Clear HWP desired on suspend/shutdown and offline
  PM: sleep: Fix runtime PM based cpuidle support
  dt-bindings: opp: Allow multi-worded OPP entry name
  opp: Fix return in _opp_add_static_v2()
  PM / devfreq: tegra30: Check whether clk_round_rate() returns zero rate
  PM / devfreq: tegra30: Use resource-managed helpers
  PM / devfreq: Add devm_devfreq_add_governor()
  opp: Add more resource-managed variants of dev_pm_opp_of_add_table()
  opp: Change type of dev_pm_opp_attach_genpd(names) argument
  opp: Fix required-opps phandle array count check
  • Loading branch information
torvalds committed Nov 10, 2021
2 parents 285fc3d + dcc0b6f commit d422555
Show file tree
Hide file tree
Showing 9 changed files with 229 additions and 108 deletions.
2 changes: 1 addition & 1 deletion Documentation/devicetree/bindings/opp/opp-v2-base.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ properties:
type: boolean

patternProperties:
'^opp-?[0-9]+$':
'^opp(-?[0-9]+)*$':
type: object
description:
One or more OPP nodes describing voltage-current-frequency combinations.
Expand Down
85 changes: 58 additions & 27 deletions drivers/base/power/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,7 @@ static void dpm_noirq_resume_devices(pm_message_t state)
dev = to_device(dpm_noirq_list.next);
get_device(dev);
list_move_tail(&dev->power.entry, &dpm_late_early_list);

mutex_unlock(&dpm_list_mtx);

if (!is_async(dev)) {
Expand All @@ -724,8 +725,9 @@ static void dpm_noirq_resume_devices(pm_message_t state)
}
}

mutex_lock(&dpm_list_mtx);
put_device(dev);

mutex_lock(&dpm_list_mtx);
}
mutex_unlock(&dpm_list_mtx);
async_synchronize_full();
Expand Down Expand Up @@ -849,6 +851,7 @@ void dpm_resume_early(pm_message_t state)
dev = to_device(dpm_late_early_list.next);
get_device(dev);
list_move_tail(&dev->power.entry, &dpm_suspended_list);

mutex_unlock(&dpm_list_mtx);

if (!is_async(dev)) {
Expand All @@ -862,8 +865,10 @@ void dpm_resume_early(pm_message_t state)
pm_dev_err(dev, state, " early", error);
}
}
mutex_lock(&dpm_list_mtx);

put_device(dev);

mutex_lock(&dpm_list_mtx);
}
mutex_unlock(&dpm_list_mtx);
async_synchronize_full();
Expand Down Expand Up @@ -1026,7 +1031,12 @@ void dpm_resume(pm_message_t state)
}
if (!list_empty(&dev->power.entry))
list_move_tail(&dev->power.entry, &dpm_prepared_list);

mutex_unlock(&dpm_list_mtx);

put_device(dev);

mutex_lock(&dpm_list_mtx);
}
mutex_unlock(&dpm_list_mtx);
async_synchronize_full();
Expand Down Expand Up @@ -1104,14 +1114,16 @@ void dpm_complete(pm_message_t state)
get_device(dev);
dev->power.is_prepared = false;
list_move(&dev->power.entry, &list);

mutex_unlock(&dpm_list_mtx);

trace_device_pm_callback_start(dev, "", state.event);
device_complete(dev, state);
trace_device_pm_callback_end(dev, 0);

mutex_lock(&dpm_list_mtx);
put_device(dev);

mutex_lock(&dpm_list_mtx);
}
list_splice(&list, &dpm_list);
mutex_unlock(&dpm_list_mtx);
Expand Down Expand Up @@ -1296,17 +1308,21 @@ static int dpm_noirq_suspend_devices(pm_message_t state)
error = device_suspend_noirq(dev);

mutex_lock(&dpm_list_mtx);

if (error) {
pm_dev_err(dev, state, " noirq", error);
dpm_save_failed_dev(dev_name(dev));
put_device(dev);
break;
}
if (!list_empty(&dev->power.entry))
} else if (!list_empty(&dev->power.entry)) {
list_move(&dev->power.entry, &dpm_noirq_list);
}

mutex_unlock(&dpm_list_mtx);

put_device(dev);

if (async_error)
mutex_lock(&dpm_list_mtx);

if (error || async_error)
break;
}
mutex_unlock(&dpm_list_mtx);
Expand Down Expand Up @@ -1463,6 +1479,7 @@ int dpm_suspend_late(pm_message_t state)
int error = 0;

trace_suspend_resume(TPS("dpm_suspend_late"), state.event, true);
wake_up_all_idle_cpus();
mutex_lock(&dpm_list_mtx);
pm_transition = state;
async_error = 0;
Expand All @@ -1471,23 +1488,28 @@ int dpm_suspend_late(pm_message_t state)
struct device *dev = to_device(dpm_suspended_list.prev);

get_device(dev);

mutex_unlock(&dpm_list_mtx);

error = device_suspend_late(dev);

mutex_lock(&dpm_list_mtx);

if (!list_empty(&dev->power.entry))
list_move(&dev->power.entry, &dpm_late_early_list);

if (error) {
pm_dev_err(dev, state, " late", error);
dpm_save_failed_dev(dev_name(dev));
put_device(dev);
break;
}

mutex_unlock(&dpm_list_mtx);

put_device(dev);

if (async_error)
mutex_lock(&dpm_list_mtx);

if (error || async_error)
break;
}
mutex_unlock(&dpm_list_mtx);
Expand Down Expand Up @@ -1747,21 +1769,27 @@ int dpm_suspend(pm_message_t state)
struct device *dev = to_device(dpm_prepared_list.prev);

get_device(dev);

mutex_unlock(&dpm_list_mtx);

error = device_suspend(dev);

mutex_lock(&dpm_list_mtx);

if (error) {
pm_dev_err(dev, state, "", error);
dpm_save_failed_dev(dev_name(dev));
put_device(dev);
break;
}
if (!list_empty(&dev->power.entry))
} else if (!list_empty(&dev->power.entry)) {
list_move(&dev->power.entry, &dpm_suspended_list);
}

mutex_unlock(&dpm_list_mtx);

put_device(dev);
if (async_error)

mutex_lock(&dpm_list_mtx);

if (error || async_error)
break;
}
mutex_unlock(&dpm_list_mtx);
Expand Down Expand Up @@ -1878,28 +1906,31 @@ int dpm_prepare(pm_message_t state)
struct device *dev = to_device(dpm_list.next);

get_device(dev);

mutex_unlock(&dpm_list_mtx);

trace_device_pm_callback_start(dev, "", state.event);
error = device_prepare(dev, state);
trace_device_pm_callback_end(dev, error);

mutex_lock(&dpm_list_mtx);
if (error) {
if (error == -EAGAIN) {
put_device(dev);
error = 0;
continue;
}

if (!error) {
dev->power.is_prepared = true;
if (!list_empty(&dev->power.entry))
list_move_tail(&dev->power.entry, &dpm_prepared_list);
} else if (error == -EAGAIN) {
error = 0;
} else {
dev_info(dev, "not prepared for power transition: code %d\n",
error);
put_device(dev);
break;
}
dev->power.is_prepared = true;
if (!list_empty(&dev->power.entry))
list_move_tail(&dev->power.entry, &dpm_prepared_list);

mutex_unlock(&dpm_list_mtx);

put_device(dev);

mutex_lock(&dpm_list_mtx);
}
mutex_unlock(&dpm_list_mtx);
trace_suspend_resume(TPS("dpm_prepare"), state.event, false);
Expand Down
36 changes: 34 additions & 2 deletions drivers/cpufreq/intel_pstate.c
Original file line number Diff line number Diff line change
Expand Up @@ -1006,9 +1006,16 @@ static void intel_pstate_hwp_offline(struct cpudata *cpu)
*/
value &= ~GENMASK_ULL(31, 24);
value |= HWP_ENERGY_PERF_PREFERENCE(cpu->epp_cached);
WRITE_ONCE(cpu->hwp_req_cached, value);
}

/*
* Clear the desired perf field in the cached HWP request value to
* prevent nonzero desired values from being leaked into the active
* mode.
*/
value &= ~HWP_DESIRED_PERF(~0L);
WRITE_ONCE(cpu->hwp_req_cached, value);

value &= ~GENMASK_ULL(31, 0);
min_perf = HWP_LOWEST_PERF(READ_ONCE(cpu->hwp_cap_cached));

Expand Down Expand Up @@ -1620,6 +1627,9 @@ static void intel_pstate_disable_hwp_interrupt(struct cpudata *cpudata)
{
unsigned long flags;

if (!boot_cpu_has(X86_FEATURE_HWP_NOTIFY))
return;

/* wrmsrl_on_cpu has to be outside spinlock as this can result in IPC */
wrmsrl_on_cpu(cpudata->cpu, MSR_HWP_INTERRUPT, 0x00);

Expand All @@ -1642,6 +1652,7 @@ static void intel_pstate_enable_hwp_interrupt(struct cpudata *cpudata)

/* wrmsrl_on_cpu has to be outside spinlock as this can result in IPC */
wrmsrl_on_cpu(cpudata->cpu, MSR_HWP_INTERRUPT, 0x01);
wrmsrl_on_cpu(cpudata->cpu, MSR_HWP_STATUS, 0);
}
}

Expand Down Expand Up @@ -3003,6 +3014,27 @@ static int intel_cpufreq_cpu_exit(struct cpufreq_policy *policy)
return intel_pstate_cpu_exit(policy);
}

static int intel_cpufreq_suspend(struct cpufreq_policy *policy)
{
intel_pstate_suspend(policy);

if (hwp_active) {
struct cpudata *cpu = all_cpu_data[policy->cpu];
u64 value = READ_ONCE(cpu->hwp_req_cached);

/*
* Clear the desired perf field in MSR_HWP_REQUEST in case
* intel_cpufreq_adjust_perf() is in use and the last value
* written by it may not be suitable.
*/
value &= ~HWP_DESIRED_PERF(~0L);
wrmsrl_on_cpu(cpu->cpu, MSR_HWP_REQUEST, value);
WRITE_ONCE(cpu->hwp_req_cached, value);
}

return 0;
}

static struct cpufreq_driver intel_cpufreq = {
.flags = CPUFREQ_CONST_LOOPS,
.verify = intel_cpufreq_verify_policy,
Expand All @@ -3012,7 +3044,7 @@ static struct cpufreq_driver intel_cpufreq = {
.exit = intel_cpufreq_cpu_exit,
.offline = intel_cpufreq_cpu_offline,
.online = intel_pstate_cpu_online,
.suspend = intel_pstate_suspend,
.suspend = intel_cpufreq_suspend,
.resume = intel_pstate_resume,
.update_limits = intel_pstate_update_limits,
.name = "intel_cpufreq",
Expand Down
26 changes: 26 additions & 0 deletions drivers/devfreq/devfreq.c
Original file line number Diff line number Diff line change
Expand Up @@ -1301,6 +1301,32 @@ int devfreq_add_governor(struct devfreq_governor *governor)
}
EXPORT_SYMBOL(devfreq_add_governor);

static void devm_devfreq_remove_governor(void *governor)
{
WARN_ON(devfreq_remove_governor(governor));
}

/**
* devm_devfreq_add_governor() - Add devfreq governor
* @dev: device which adds devfreq governor
* @governor: the devfreq governor to be added
*
* This is a resource-managed variant of devfreq_add_governor().
*/
int devm_devfreq_add_governor(struct device *dev,
struct devfreq_governor *governor)
{
int err;

err = devfreq_add_governor(governor);
if (err)
return err;

return devm_add_action_or_reset(dev, devm_devfreq_remove_governor,
governor);
}
EXPORT_SYMBOL(devm_devfreq_add_governor);

/**
* devfreq_remove_governor() - Remove devfreq feature from a device.
* @governor: the devfreq governor to be removed
Expand Down
3 changes: 3 additions & 0 deletions drivers/devfreq/governor.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ void devfreq_update_interval(struct devfreq *devfreq, unsigned int *delay);
int devfreq_add_governor(struct devfreq_governor *governor);
int devfreq_remove_governor(struct devfreq_governor *governor);

int devm_devfreq_add_governor(struct device *dev,
struct devfreq_governor *governor);

int devfreq_update_status(struct devfreq *devfreq, unsigned long freq);
int devfreq_update_target(struct devfreq *devfreq, unsigned long freq);

Expand Down
Loading

0 comments on commit d422555

Please sign in to comment.