Page MenuHomeFreeBSD

D55477.diff
No OneTemporary

D55477.diff

diff --git a/sys/x86/cpufreq/hwpstate_amd.c b/sys/x86/cpufreq/hwpstate_amd.c
--- a/sys/x86/cpufreq/hwpstate_amd.c
+++ b/sys/x86/cpufreq/hwpstate_amd.c
@@ -73,13 +73,13 @@
#include <contrib/dev/acpica/include/acpi.h>
#include <dev/acpica/acpivar.h>
+#include <dev/acpica/acpi_cppc_lib.h>
#include <x86/cpufreq/hwpstate_common.h>
#include "acpi_if.h"
#include "cpufreq_if.h"
-
#define MSR_AMD_10H_11H_LIMIT 0xc0010061
#define MSR_AMD_10H_11H_CONTROL 0xc0010062
#define MSR_AMD_10H_11H_STATUS 0xc0010063
@@ -150,6 +150,7 @@
};
#define HWPFL_USE_CPPC (1 << 0)
+#define HWPFL_CPPC_CPUFREQ_WRITE (1 << 1)
/*
* Atomicity is achieved by only modifying a given softc on its associated CPU
@@ -168,6 +169,8 @@
};
struct {
uint64_t request;
+ uint32_t caps1;
+ struct acpi_cppc_ctx *acpi_ctx;
} cppc;
};
};
@@ -286,6 +289,42 @@
#define HWP_ERROR_CPPC_REQUEST (1 << 2)
#define HWP_ERROR_CPPC_REQUEST_WRITE (1 << 3)
+static int
+upscale_cppc_freq(struct hwpstate_softc *sc, uint32_t perf)
+{
+ struct acpi_cppc_ctx *ctx = sc->cppc.acpi_ctx;
+ uint32_t low_freq, nominal_freq, low_perf, nominal_perf;
+
+ low_freq = acpi_cppc_read_reg(ctx, ACPI_CPPC_LOWEST_FREQ);
+ nominal_freq = acpi_cppc_read_reg(ctx, ACPI_CPPC_NOMINAL_FREQ);
+ low_perf = BITS_VALUE(AMD_CPPC_CAPS_1_LOWEST_PERF_BITS, sc->cppc.caps1);
+ nominal_perf = BITS_VALUE(AMD_CPPC_CAPS_1_NOMINAL_PERF_BITS,
+ sc->cppc.caps1);
+ if (nominal_perf == low_perf)
+ nominal_perf = low_perf + 1;
+ return (low_freq +
+ (nominal_freq - low_freq) * (perf - low_perf) /
+ (nominal_perf - low_perf));
+}
+
+static int
+downscale_cppc_freq(struct hwpstate_softc *sc, uint32_t freq)
+{
+ struct acpi_cppc_ctx *ctx = sc->cppc.acpi_ctx;
+ uint32_t low_freq, nominal_freq, low_perf, nominal_perf;
+
+ low_freq = acpi_cppc_read_reg(ctx, ACPI_CPPC_LOWEST_FREQ);
+ nominal_freq = acpi_cppc_read_reg(ctx, ACPI_CPPC_NOMINAL_FREQ);
+ low_perf = BITS_VALUE(AMD_CPPC_CAPS_1_LOWEST_PERF_BITS, sc->cppc.caps1);
+ nominal_perf = BITS_VALUE(AMD_CPPC_CAPS_1_NOMINAL_PERF_BITS,
+ sc->cppc.caps1);
+ if (nominal_freq == low_freq)
+ nominal_freq = low_freq + 1;
+ return (low_perf +
+ (nominal_perf - low_perf) * (freq - low_freq) /
+ (nominal_freq - low_freq));
+}
+
static inline bool
hwp_has_error(u_int res, u_int err)
{
@@ -294,7 +333,6 @@
struct get_cppc_regs_data {
uint64_t enable;
- uint64_t caps;
uint64_t req;
/* HWP_ERROR_CPPC_* except HWP_ERROR_*_WRITE */
u_int res;
@@ -312,10 +350,6 @@
if (error != 0)
data->res |= HWP_ERROR_CPPC_ENABLE;
- error = rdmsr_safe(MSR_AMD_CPPC_CAPS_1, &data->caps);
- if (error != 0)
- data->res |= HWP_ERROR_CPPC_CAPS;
-
error = rdmsr_safe(MSR_AMD_CPPC_REQUEST, &data->req);
if (error != 0)
data->res |= HWP_ERROR_CPPC_REQUEST;
@@ -351,7 +385,7 @@
if (hwp_has_error(data.res, HWP_ERROR_CPPC_CAPS))
print_cppc_no_caps_1(sb);
else
- print_cppc_caps_1(sb, data.caps);
+ print_cppc_caps_1(sb, sc->cppc.caps1);
if (hwp_has_error(data.res, HWP_ERROR_CPPC_REQUEST))
print_cppc_no_request(sb);
@@ -562,15 +596,24 @@
if (cf == NULL)
return (EINVAL);
sc = device_get_softc(dev);
- if ((sc->flags & HWPFL_USE_CPPC) != 0)
- return (EOPNOTSUPP);
+ if ((sc->flags & HWPFL_USE_CPPC) != 0) {
+ if ((sc->flags & HWPFL_CPPC_CPUFREQ_WRITE) == 0)
+ return (ENOTSUP);
+
+ SET_BITS_VALUE(sc->cppc.request, AMD_CPPC_REQUEST_DES_PERF_BITS,
+ downscale_cppc_freq(sc, cf->freq));
+ return (x86_msr_op(MSR_AMD_CPPC_REQUEST,
+ MSR_OP_RENDEZVOUS_ONE | MSR_OP_READ |
+ MSR_OP_CPUID(cpu_get_pcpu(dev)->pc_cpuid),
+ sc->cppc.request, NULL));
+ }
+
set = sc->hwpstate_settings;
for (i = 0; i < sc->cfnum; i++)
if (CPUFREQ_CMP(cf->freq, set[i].freq))
break;
if (i == sc->cfnum)
return (EINVAL);
-
return (hwpstate_goto_pstate(dev, set[i].pstate_id));
}
@@ -614,30 +657,59 @@
return (0);
}
+#define AMD_CPPC_STEPS 10
+#define AMD_CPPC_COUNT_LEVELS(l, r) ((r - l) / AMD_CPPC_STEPS + 1)
+
static int
hwpstate_settings(device_t dev, struct cf_setting *sets, int *count)
{
struct hwpstate_softc *sc;
struct hwpstate_setting set;
int i;
+ int levels;
+ int cur_level;
if (sets == NULL || count == NULL)
return (EINVAL);
sc = device_get_softc(dev);
- if ((sc->flags & HWPFL_USE_CPPC) != 0)
- return (EOPNOTSUPP);
-
- if (*count < sc->cfnum)
- return (E2BIG);
- for (i = 0; i < sc->cfnum; i++, sets++) {
- set = sc->hwpstate_settings[i];
- sets->freq = set.freq;
- sets->volts = set.volts;
- sets->power = set.power;
- sets->lat = set.lat;
- sets->dev = dev;
+
+ if ((sc->flags & HWPFL_USE_CPPC) != 0) {
+ if ((sc->flags & HWPFL_CPPC_CPUFREQ_WRITE) == 0)
+ return (ENOTSUP);
+ levels = AMD_CPPC_COUNT_LEVELS(
+ BITS_VALUE(AMD_CPPC_CAPS_1_LOWEST_PERF_BITS,
+ sc->cppc.caps1),
+ BITS_VALUE(AMD_CPPC_CAPS_1_HIGHEST_PERF_BITS,
+ sc->cppc.caps1));
+ if (*count < levels)
+ return (E2BIG);
+ *count = levels;
+ cur_level = BITS_VALUE(AMD_CPPC_CAPS_1_LOWEST_PERF_BITS,
+ sc->cppc.caps1);
+ for (i = 0; i < levels; i++, sets++) {
+ sets->freq = upscale_cppc_freq(sc, cur_level);
+ sets->volts = CPUFREQ_VAL_UNKNOWN;
+ sets->power = CPUFREQ_VAL_UNKNOWN;
+ sets->lat = CPUFREQ_VAL_UNKNOWN;
+ sets->dev = dev;
+ cur_level += AMD_CPPC_STEPS;
+ cur_level = MIN(cur_level,
+ BITS_VALUE(AMD_CPPC_CAPS_1_HIGHEST_PERF_BITS,
+ sc->cppc.caps1));
+ }
+ } else {
+ if (*count < sc->cfnum)
+ return (E2BIG);
+ for (i = 0; i < sc->cfnum; i++, sets++) {
+ set = sc->hwpstate_settings[i];
+ sets->freq = set.freq;
+ sets->volts = set.volts;
+ sets->power = set.power;
+ sets->lat = set.lat;
+ sets->dev = dev;
+ }
+ *count = sc->cfnum;
}
- *count = sc->cfnum;
return (0);
}
@@ -649,12 +721,14 @@
if (type == NULL)
return (EINVAL);
+ *type = CPUFREQ_TYPE_ABSOLUTE;
+
sc = device_get_softc(dev);
+ if ((sc->flags & HWPFL_USE_CPPC)) {
+ if ((sc->flags & HWPFL_CPPC_CPUFREQ_WRITE) == 0)
+ *type |= CPUFREQ_FLAG_INFO_ONLY | CPUFREQ_FLAG_UNCACHED;
+ }
- *type = CPUFREQ_TYPE_ABSOLUTE;
- *type |= (sc->flags & HWPFL_USE_CPPC) != 0 ?
- CPUFREQ_FLAG_INFO_ONLY | CPUFREQ_FLAG_UNCACHED :
- 0;
return (0);
}
@@ -752,6 +826,7 @@
lowest_perf = 0;
highest_perf = -1;
}
+ sc->cppc.caps1 = data->caps;
SET_BITS_VALUE(sc->cppc.request, AMD_CPPC_REQUEST_MIN_PERF_BITS,
lowest_perf);
SET_BITS_VALUE(sc->cppc.request, AMD_CPPC_REQUEST_MAX_PERF_BITS,
@@ -905,6 +980,7 @@
{
struct hwpstate_softc *sc;
int res;
+ uint32_t flag;
sc = device_get_softc(dev);
if ((sc->flags & HWPFL_USE_CPPC) != 0) {
@@ -954,6 +1030,19 @@
"0 enables autonomous mode, otherwise value should be "
"between 'minimum_performance' and 'maximum_performance' "
"inclusive)");
+ sc->cppc.acpi_ctx = acpi_cppc_ctx_init(dev);
+ if (sc->cppc.acpi_ctx) {
+ if (hwpstate_verbose)
+ device_printf(dev, "Found ACPI _CPC object");
+ flag = acpi_cppc_get_features(sc->cppc.acpi_ctx);
+ if ((flag & ACPI_CPPC_HAS_LOW_FREQ) &&
+ (flag & ACPI_CPPC_HAS_NOMINAL_FREQ)) {
+ if (hwpstate_verbose)
+ device_printf(dev,
+ "ACPI CPPC is writable in cpufreq");
+ sc->flags |= HWPFL_CPPC_CPUFREQ_WRITE;
+ }
+ }
}
return (cpufreq_register(dev));
}
@@ -1103,6 +1192,8 @@
sc = device_get_softc(dev);
if ((sc->flags & HWPFL_USE_CPPC) == 0)
hwpstate_goto_pstate(dev, 0);
+ else if (sc->cppc.acpi_ctx)
+ acpi_cppc_ctx_free(dev, sc->cppc.acpi_ctx);
return (cpufreq_unregister(dev));
}

File Metadata

Mime Type
text/plain
Expires
Thu, Feb 26, 10:46 AM (30 m, 26 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
29003764
Default Alt Text
D55477.diff (7 KB)

Event Timeline