Commit d422555f authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
parents 285fc3db dcc0b6f2
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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.
+58 −27
Original line number Diff line number Diff line
@@ -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)) {
@@ -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();
@@ -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)) {
@@ -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();
@@ -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();
@@ -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);
@@ -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);
@@ -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;
@@ -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);
@@ -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);
@@ -1878,6 +1906,7 @@ 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);
@@ -1885,21 +1914,23 @@ int dpm_prepare(pm_message_t state)
		trace_device_pm_callback_end(dev, error);

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

		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;
				continue;
			}
		} 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);
+34 −2
Original line number Diff line number Diff line
@@ -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));

@@ -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);

@@ -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);
	}
}

@@ -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,
@@ -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",
+26 −0
Original line number Diff line number Diff line
@@ -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
+3 −0
Original line number Diff line number Diff line
@@ -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);

Loading