Commit 8e5b4779 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

Merge branches 'pm-cpufreq' and 'pm-cpuidle'

Merge cpufreq and cpuidle updates for 5.16-rc1:

 - Fix cpu->pstate.turbo_freq initialization in intel_pstate (Zhang
   Rui).

 - Make intel_pstate process HWP Guaranteed change notifications from
   the processor (Srinivas Pandruvada).

 - Fix typo in cpufreq.h (Rafael Wysocki).

 - Fix tegra driver to handle BPMP errors properly (Mikko Perttunen).

 - Fix the parameter usage of the newly added perf-domain API (Hector
   Yuan).

 - Minor cleanups to cppc, vexpress and s3c244x drivers (Han Wang,
   Guenter Roeck, and Arnd Bergmann).

 - Fix kobject memory leaks in cpuidle error paths (Anel Orazgaliyeva).

 - Make intel_idle enable interrupts before entering C1 on some Xeon
   processor models (Artem Bityutskiy).

* pm-cpufreq:
  cpufreq: Fix parameter in parse_perf_domain()
  cpufreq: intel_pstate: Fix cpu->pstate.turbo_freq initialization
  cpufreq: Fix typo in cpufreq.h
  cpufreq: intel_pstate: Process HWP Guaranteed change notification
  cpufreq: tegra186/tegra194: Handle errors in BPMP response
  cpufreq: remove useless INIT_LIST_HEAD()
  cpufreq: s3c244x: add fallthrough comments for switch
  cpufreq: vexpress: Drop unused variable

* pm-cpuidle:
  cpuidle: Fix kobject memory leaks in error paths
  intel_idle: enable interrupts before C1 on Xeons
Loading
Loading
Loading
Loading
+0 −2
Original line number Diff line number Diff line
@@ -741,8 +741,6 @@ static int __init cppc_cpufreq_init(void)
	if ((acpi_disabled) || !acpi_cpc_valid())
		return -ENODEV;

	INIT_LIST_HEAD(&cpu_data_list);

	cppc_check_hisi_workaround();
	cppc_freq_invariance_init();

+113 −7
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@
#include <asm/cpu_device_id.h>
#include <asm/cpufeature.h>
#include <asm/intel-family.h>
#include "../drivers/thermal/intel/thermal_interrupt.h"

#define INTEL_PSTATE_SAMPLING_INTERVAL	(10 * NSEC_PER_MSEC)

@@ -219,6 +220,7 @@ struct global_params {
 * @sched_flags:	Store scheduler flags for possible cross CPU update
 * @hwp_boost_min:	Last HWP boosted min performance
 * @suspended:		Whether or not the driver has been suspended.
 * @hwp_notify_work:	workqueue for HWP notifications.
 *
 * This structure stores per CPU instance data for all CPUs.
 */
@@ -257,6 +259,7 @@ struct cpudata {
	unsigned int sched_flags;
	u32 hwp_boost_min;
	bool suspended;
	struct delayed_work hwp_notify_work;
};

static struct cpudata **all_cpu_data;
@@ -537,7 +540,8 @@ static void intel_pstate_hybrid_hwp_adjust(struct cpudata *cpu)
	 * scaling factor is too high, so recompute it to make the HWP_CAP
	 * highest performance correspond to the maximum turbo frequency.
	 */
	if (turbo_freq < cpu->pstate.turbo_pstate * scaling) {
	cpu->pstate.turbo_freq = cpu->pstate.turbo_pstate * scaling;
	if (turbo_freq < cpu->pstate.turbo_freq) {
		cpu->pstate.turbo_freq = turbo_freq;
		scaling = DIV_ROUND_UP(turbo_freq, cpu->pstate.turbo_pstate);
		cpu->pstate.scaling = scaling;
@@ -985,11 +989,15 @@ static void intel_pstate_hwp_set(unsigned int cpu)
	wrmsrl_on_cpu(cpu, MSR_HWP_REQUEST, value);
}

static void intel_pstate_disable_hwp_interrupt(struct cpudata *cpudata);

static void intel_pstate_hwp_offline(struct cpudata *cpu)
{
	u64 value = READ_ONCE(cpu->hwp_req_cached);
	int min_perf;

	intel_pstate_disable_hwp_interrupt(cpu);

	if (boot_cpu_has(X86_FEATURE_HWP_EPP)) {
		/*
		 * In case the EPP has been set to "performance" by the
@@ -1053,6 +1061,9 @@ static int intel_pstate_suspend(struct cpufreq_policy *policy)

	cpu->suspended = true;

	/* disable HWP interrupt and cancel any pending work */
	intel_pstate_disable_hwp_interrupt(cpu);

	return 0;
}

@@ -1546,15 +1557,105 @@ static void intel_pstate_sysfs_hide_hwp_dynamic_boost(void)

/************************** sysfs end ************************/

static void intel_pstate_notify_work(struct work_struct *work)
{
	struct cpudata *cpudata =
		container_of(to_delayed_work(work), struct cpudata, hwp_notify_work);

	cpufreq_update_policy(cpudata->cpu);
	wrmsrl_on_cpu(cpudata->cpu, MSR_HWP_STATUS, 0);
}

static DEFINE_SPINLOCK(hwp_notify_lock);
static cpumask_t hwp_intr_enable_mask;

void notify_hwp_interrupt(void)
{
	unsigned int this_cpu = smp_processor_id();
	struct cpudata *cpudata;
	unsigned long flags;
	u64 value;

	if (!READ_ONCE(hwp_active) || !boot_cpu_has(X86_FEATURE_HWP_NOTIFY))
		return;

	rdmsrl_safe(MSR_HWP_STATUS, &value);
	if (!(value & 0x01))
		return;

	spin_lock_irqsave(&hwp_notify_lock, flags);

	if (!cpumask_test_cpu(this_cpu, &hwp_intr_enable_mask))
		goto ack_intr;

	/*
	 * Currently we never free all_cpu_data. And we can't reach here
	 * without this allocated. But for safety for future changes, added
	 * check.
	 */
	if (unlikely(!READ_ONCE(all_cpu_data)))
		goto ack_intr;

	/*
	 * The free is done during cleanup, when cpufreq registry is failed.
	 * We wouldn't be here if it fails on init or switch status. But for
	 * future changes, added check.
	 */
	cpudata = READ_ONCE(all_cpu_data[this_cpu]);
	if (unlikely(!cpudata))
		goto ack_intr;

	schedule_delayed_work(&cpudata->hwp_notify_work, msecs_to_jiffies(10));

	spin_unlock_irqrestore(&hwp_notify_lock, flags);

	return;

ack_intr:
	wrmsrl_safe(MSR_HWP_STATUS, 0);
	spin_unlock_irqrestore(&hwp_notify_lock, flags);
}

static void intel_pstate_disable_hwp_interrupt(struct cpudata *cpudata)
{
	unsigned long flags;

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

	spin_lock_irqsave(&hwp_notify_lock, flags);
	if (cpumask_test_and_clear_cpu(cpudata->cpu, &hwp_intr_enable_mask))
		cancel_delayed_work(&cpudata->hwp_notify_work);
	spin_unlock_irqrestore(&hwp_notify_lock, flags);
}

static void intel_pstate_enable_hwp_interrupt(struct cpudata *cpudata)
{
	/* Enable HWP notification interrupt for guaranteed performance change */
	if (boot_cpu_has(X86_FEATURE_HWP_NOTIFY)) {
		unsigned long flags;

		spin_lock_irqsave(&hwp_notify_lock, flags);
		INIT_DELAYED_WORK(&cpudata->hwp_notify_work, intel_pstate_notify_work);
		cpumask_set_cpu(cpudata->cpu, &hwp_intr_enable_mask);
		spin_unlock_irqrestore(&hwp_notify_lock, flags);

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

static void intel_pstate_hwp_enable(struct cpudata *cpudata)
{
	/* First disable HWP notification interrupt as we don't process them */
	/* First disable HWP notification interrupt till we activate again */
	if (boot_cpu_has(X86_FEATURE_HWP_NOTIFY))
		wrmsrl_on_cpu(cpudata->cpu, MSR_HWP_INTERRUPT, 0x00);

	wrmsrl_on_cpu(cpudata->cpu, MSR_PM_ENABLE, 0x1);
	if (cpudata->epp_default == -EINVAL)
		cpudata->epp_default = intel_pstate_get_epp(cpudata, 0);

	intel_pstate_enable_hwp_interrupt(cpudata);
}

static int atom_get_min_pstate(void)
@@ -2266,7 +2367,7 @@ static int intel_pstate_init_cpu(unsigned int cpunum)
		if (!cpu)
			return -ENOMEM;

		all_cpu_data[cpunum] = cpu;
		WRITE_ONCE(all_cpu_data[cpunum], cpu);

		cpu->cpu = cpunum;

@@ -2929,8 +3030,10 @@ static void intel_pstate_driver_cleanup(void)
			if (intel_pstate_driver == &intel_pstate)
				intel_pstate_clear_update_util_hook(cpu);

			spin_lock(&hwp_notify_lock);
			kfree(all_cpu_data[cpu]);
			all_cpu_data[cpu] = NULL;
			WRITE_ONCE(all_cpu_data[cpu], NULL);
			spin_unlock(&hwp_notify_lock);
		}
	}
	cpus_read_unlock();
@@ -3199,6 +3302,7 @@ static bool intel_pstate_hwp_is_enabled(void)

static int __init intel_pstate_init(void)
{
	static struct cpudata **_all_cpu_data;
	const struct x86_cpu_id *id;
	int rc;

@@ -3224,7 +3328,7 @@ static int __init intel_pstate_init(void)
		 * deal with it.
		 */
		if ((!no_hwp && boot_cpu_has(X86_FEATURE_HWP_EPP)) || hwp_forced) {
			hwp_active++;
			WRITE_ONCE(hwp_active, 1);
			hwp_mode_bdw = id->driver_data;
			intel_pstate.attr = hwp_cpufreq_attrs;
			intel_cpufreq.attr = hwp_cpufreq_attrs;
@@ -3275,10 +3379,12 @@ static int __init intel_pstate_init(void)

	pr_info("Intel P-state driver initializing\n");

	all_cpu_data = vzalloc(array_size(sizeof(void *), num_possible_cpus()));
	if (!all_cpu_data)
	_all_cpu_data = vzalloc(array_size(sizeof(void *), num_possible_cpus()));
	if (!_all_cpu_data)
		return -ENOMEM;

	WRITE_ONCE(all_cpu_data, _all_cpu_data);

	intel_pstate_request_control_from_smm();

	intel_pstate_sysfs_expose_params();
+2 −0
Original line number Diff line number Diff line
@@ -173,12 +173,14 @@ static void s3c2440_cpufreq_setdivs(struct s3c_cpufreq_config *cfg)

	case 6:
		camdiv |= S3C2440_CAMDIVN_HCLK3_HALF;
		fallthrough;
	case 3:
		clkdiv |= S3C2440_CLKDIVN_HDIVN_3_6;
		break;

	case 8:
		camdiv |= S3C2440_CAMDIVN_HCLK4_HALF;
		fallthrough;
	case 4:
		clkdiv |= S3C2440_CLKDIVN_HDIVN_4_8;
		break;
+4 −0
Original line number Diff line number Diff line
@@ -159,6 +159,10 @@ static struct cpufreq_frequency_table *init_vhint_table(
		table = ERR_PTR(err);
		goto free;
	}
	if (msg.rx.ret) {
		table = ERR_PTR(-EINVAL);
		goto free;
	}

	for (i = data->vfloor; i <= data->vceil; i++) {
		u16 ndiv = data->ndiv[i];
+7 −1
Original line number Diff line number Diff line
@@ -242,7 +242,7 @@ static int tegra194_cpufreq_init(struct cpufreq_policy *policy)

	smp_call_function_single(policy->cpu, get_cpu_cluster, &cl, true);

	if (cl >= data->num_clusters)
	if (cl >= data->num_clusters || !data->tables[cl])
		return -EINVAL;

	/* set same policy for all cpus in a cluster */
@@ -310,6 +310,12 @@ init_freq_table(struct platform_device *pdev, struct tegra_bpmp *bpmp,
	err = tegra_bpmp_transfer(bpmp, &msg);
	if (err)
		return ERR_PTR(err);
	if (msg.rx.ret == -BPMP_EINVAL) {
		/* Cluster not available */
		return NULL;
	}
	if (msg.rx.ret)
		return ERR_PTR(-EINVAL);

	/*
	 * Make sure frequency table step is a multiple of mdiv to match
Loading