Commit b9f103d0 authored by Gautham R. Shenoy's avatar Gautham R. Shenoy Committed by Mario Limonciello (AMD)
Browse files

amd-pstate: Add sysfs support for floor_freq and floor_count



When Floor Performance feature is supported by the platform, expose
two sysfs files:

   * amd_pstate_floor_freq to allow userspace to request the floor
     frequency for each CPU.

   * amd_pstate_floor_count which advertises the number of distinct
     levels of floor frequencies supported on this platform.

Reset the floor_perf to bios_floor_perf in the suspend, offline, and
exit paths, and restore the value to the cached user-request
floor_freq on the resume and online paths mirroring how bios_min_perf
is handled for MSR_AMD_CPPC_REQ.

Reviewed-by: default avatarMario Limonciello (AMD) <superm1@kernel.org>
Signed-off-by: default avatarGautham R. Shenoy <gautham.shenoy@amd.com>
Signed-off-by: default avatarMario Limonciello (AMD) <superm1@kernel.org>
parent 97838281
Loading
Loading
Loading
Loading
+87 −6
Original line number Diff line number Diff line
@@ -383,8 +383,10 @@ static int amd_pstate_init_floor_perf(struct cpufreq_policy *policy)
			return ret;
	}

	cpudata->bios_floor_perf = floor_perf;

	cpudata->bios_floor_perf = floor_perf;
	cpudata->floor_freq = perf_to_freq(cpudata->perf, cpudata->nominal_freq,
					   floor_perf);
	return 0;
}

@@ -1288,6 +1290,46 @@ static ssize_t show_energy_performance_preference(
	return sysfs_emit(buf, "%s\n", energy_perf_strings[preference]);
}

static ssize_t store_amd_pstate_floor_freq(struct cpufreq_policy *policy,
					   const char *buf, size_t count)
{
	struct amd_cpudata *cpudata = policy->driver_data;
	union perf_cached perf = READ_ONCE(cpudata->perf);
	unsigned int freq;
	u8 floor_perf;
	int ret;

	ret = kstrtouint(buf, 0, &freq);
	if (ret)
		return ret;

	if (freq < policy->cpuinfo.min_freq || freq > policy->max)
		return -EINVAL;

	floor_perf = freq_to_perf(perf, cpudata->nominal_freq, freq);
	ret = amd_pstate_set_floor_perf(policy, floor_perf);

	if (!ret)
		cpudata->floor_freq = freq;

	return ret ?: count;
}

static ssize_t show_amd_pstate_floor_freq(struct cpufreq_policy *policy, char *buf)
{
	struct amd_cpudata *cpudata = policy->driver_data;

	return sysfs_emit(buf, "%u\n", cpudata->floor_freq);
}

static ssize_t show_amd_pstate_floor_count(struct cpufreq_policy *policy, char *buf)
{
	struct amd_cpudata *cpudata = policy->driver_data;
	u8 count = cpudata->floor_perf_cnt;

	return sysfs_emit(buf, "%u\n", count);
}

cpufreq_freq_attr_ro(amd_pstate_max_freq);
cpufreq_freq_attr_ro(amd_pstate_lowest_nonlinear_freq);

@@ -1296,6 +1338,8 @@ cpufreq_freq_attr_ro(amd_pstate_prefcore_ranking);
cpufreq_freq_attr_ro(amd_pstate_hw_prefcore);
cpufreq_freq_attr_rw(energy_performance_preference);
cpufreq_freq_attr_ro(energy_performance_available_preferences);
cpufreq_freq_attr_rw(amd_pstate_floor_freq);
cpufreq_freq_attr_ro(amd_pstate_floor_count);

struct freq_attr_visibility {
	struct freq_attr *attr;
@@ -1320,6 +1364,12 @@ static bool epp_visibility(void)
	return cppc_state == AMD_PSTATE_ACTIVE;
}

/* Determines whether amd_pstate_floor_freq related attributes should be visible */
static bool floor_freq_visibility(void)
{
	return cpu_feature_enabled(X86_FEATURE_CPPC_PERF_PRIO);
}

static struct freq_attr_visibility amd_pstate_attr_visibility[] = {
	{&amd_pstate_max_freq, always_visible},
	{&amd_pstate_lowest_nonlinear_freq, always_visible},
@@ -1328,6 +1378,8 @@ static struct freq_attr_visibility amd_pstate_attr_visibility[] = {
	{&amd_pstate_hw_prefcore, prefcore_visibility},
	{&energy_performance_preference, epp_visibility},
	{&energy_performance_available_preferences, epp_visibility},
	{&amd_pstate_floor_freq, floor_freq_visibility},
	{&amd_pstate_floor_count, floor_freq_visibility},
};

static struct freq_attr **get_freq_attrs(void)
@@ -1748,24 +1800,39 @@ static int amd_pstate_epp_set_policy(struct cpufreq_policy *policy)

static int amd_pstate_cpu_online(struct cpufreq_policy *policy)
{
	return amd_pstate_cppc_enable(policy);
	struct amd_cpudata *cpudata = policy->driver_data;
	union perf_cached perf = READ_ONCE(cpudata->perf);
	u8 cached_floor_perf;
	int ret;

	ret = amd_pstate_cppc_enable(policy);
	if (ret)
		return ret;

	cached_floor_perf = freq_to_perf(perf, cpudata->nominal_freq, cpudata->floor_freq);
	return amd_pstate_set_floor_perf(policy, cached_floor_perf);
}

static int amd_pstate_cpu_offline(struct cpufreq_policy *policy)
{
	struct amd_cpudata *cpudata = policy->driver_data;
	union perf_cached perf = READ_ONCE(cpudata->perf);
	int ret;

	/*
	 * Reset CPPC_REQ MSR to the BIOS value, this will allow us to retain the BIOS specified
	 * min_perf value across kexec reboots. If this CPU is just onlined normally after this, the
	 * limits, epp and desired perf will get reset to the cached values in cpudata struct
	 */
	return amd_pstate_update_perf(policy, perf.bios_min_perf,
	ret = amd_pstate_update_perf(policy, perf.bios_min_perf,
				     FIELD_GET(AMD_CPPC_DES_PERF_MASK, cpudata->cppc_req_cached),
				     FIELD_GET(AMD_CPPC_MAX_PERF_MASK, cpudata->cppc_req_cached),
				     FIELD_GET(AMD_CPPC_EPP_PERF_MASK, cpudata->cppc_req_cached),
				     false);
	if (ret)
		return ret;

	return amd_pstate_set_floor_perf(policy, cpudata->bios_floor_perf);
}

static int amd_pstate_suspend(struct cpufreq_policy *policy)
@@ -1787,6 +1854,10 @@ static int amd_pstate_suspend(struct cpufreq_policy *policy)
	if (ret)
		return ret;

	ret = amd_pstate_set_floor_perf(policy, cpudata->bios_floor_perf);
	if (ret)
		return ret;

	/* set this flag to avoid setting core offline*/
	cpudata->suspended = true;

@@ -1798,15 +1869,24 @@ static int amd_pstate_resume(struct cpufreq_policy *policy)
	struct amd_cpudata *cpudata = policy->driver_data;
	union perf_cached perf = READ_ONCE(cpudata->perf);
	int cur_perf = freq_to_perf(perf, cpudata->nominal_freq, policy->cur);
	u8 cached_floor_perf;
	int ret;

	/* Set CPPC_REQ to last sane value until the governor updates it */
	return amd_pstate_update_perf(policy, perf.min_limit_perf, cur_perf, perf.max_limit_perf,
	ret = amd_pstate_update_perf(policy, perf.min_limit_perf, cur_perf, perf.max_limit_perf,
				     0U, false);
	if (ret)
		return ret;

	cached_floor_perf = freq_to_perf(perf, cpudata->nominal_freq, cpudata->floor_freq);
	return amd_pstate_set_floor_perf(policy, cached_floor_perf);
}

static int amd_pstate_epp_resume(struct cpufreq_policy *policy)
{
	struct amd_cpudata *cpudata = policy->driver_data;
	union perf_cached perf = READ_ONCE(cpudata->perf);
	u8 cached_floor_perf;

	if (cpudata->suspended) {
		int ret;
@@ -1819,7 +1899,8 @@ static int amd_pstate_epp_resume(struct cpufreq_policy *policy)
		cpudata->suspended = false;
	}

	return 0;
	cached_floor_perf = freq_to_perf(perf, cpudata->nominal_freq, cpudata->floor_freq);
	return amd_pstate_set_floor_perf(policy, cached_floor_perf);
}

static struct cpufreq_driver amd_pstate_driver = {
+2 −0
Original line number Diff line number Diff line
@@ -74,6 +74,7 @@ struct amd_aperf_mperf {
 * @max_limit_freq: Cached value of policy->max (in khz)
 * @nominal_freq: the frequency (in khz) that mapped to nominal_perf
 * @lowest_nonlinear_freq: the frequency (in khz) that mapped to lowest_nonlinear_perf
 * @floor_freq: Cached value of the user requested floor_freq
 * @cur: Difference of Aperf/Mperf/tsc count between last and current sample
 * @prev: Last Aperf/Mperf/tsc count value read from register
 * @freq: current cpu frequency value (in khz)
@@ -103,6 +104,7 @@ struct amd_cpudata {
	u32	max_limit_freq;
	u32	nominal_freq;
	u32	lowest_nonlinear_freq;
	u32	floor_freq;

	struct amd_aperf_mperf cur;
	struct amd_aperf_mperf prev;