mirror of
https://github.com/freebsd/freebsd-src.git
synced 2026-11-07 12:48:33 -10:00
hwpstate_amd: Refactor the cpufreq code by using delegation pattenr
We separate the code of CPPC and legacy pstate driver to make it easier to read. Reviewed by: olce Approved by: markj (mentor) Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D55604
This commit is contained in:
1 parent
3797fe720a
commit
191f47bcd6
1 file changed
+149
-78
+149
-78
@@ -152,6 +152,13 @@ struct hwpstate_setting {
|
||||
#define HWPFL_USE_CPPC (1 << 0)
|
||||
#define HWPFL_CPPC_REQUEST_NOT_READ (1 << 1)
|
||||
|
||||
struct hwpstate_cpufreq_methods {
|
||||
int (*get)(device_t dev, struct cf_setting *cf);
|
||||
int (*set)(device_t dev, const struct cf_setting *cf);
|
||||
int (*settings)(device_t dev, struct cf_setting *sets, int *count);
|
||||
int (*type)(device_t dev, int *type);
|
||||
};
|
||||
|
||||
/*
|
||||
* Atomicity is achieved by only modifying a given softc on its associated CPU
|
||||
* and with interrupts disabled.
|
||||
@@ -161,6 +168,7 @@ struct hwpstate_setting {
|
||||
struct hwpstate_softc {
|
||||
device_t dev;
|
||||
u_int flags;
|
||||
const struct hwpstate_cpufreq_methods *cpufreq_methods;
|
||||
union {
|
||||
struct {
|
||||
struct hwpstate_setting
|
||||
@@ -620,80 +628,105 @@ hwpstate_goto_pstate(device_t dev, int id)
|
||||
}
|
||||
|
||||
static int
|
||||
hwpstate_set(device_t dev, const struct cf_setting *cf)
|
||||
hwpstate_set_cppc(device_t dev __unused, const struct cf_setting *cf __unused)
|
||||
{
|
||||
return (EOPNOTSUPP);
|
||||
}
|
||||
|
||||
static int
|
||||
hwpstate_set_pstate(device_t dev, const struct cf_setting *cf)
|
||||
{
|
||||
struct hwpstate_softc *sc;
|
||||
struct hwpstate_setting *set;
|
||||
int i;
|
||||
|
||||
if (cf == NULL)
|
||||
return (EINVAL);
|
||||
sc = device_get_softc(dev);
|
||||
if ((sc->flags & HWPFL_USE_CPPC) != 0)
|
||||
return (EOPNOTSUPP);
|
||||
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));
|
||||
}
|
||||
|
||||
static int
|
||||
hwpstate_set(device_t dev, const struct cf_setting *cf)
|
||||
{
|
||||
struct hwpstate_softc *sc = device_get_softc(dev);
|
||||
|
||||
if (cf == NULL)
|
||||
return (EINVAL);
|
||||
return (sc->cpufreq_methods->set(dev, cf));
|
||||
}
|
||||
|
||||
static int
|
||||
hwpstate_get_cppc(device_t dev, struct cf_setting *cf)
|
||||
{
|
||||
struct pcpu *pc;
|
||||
uint64_t rate;
|
||||
int ret;
|
||||
|
||||
pc = cpu_get_pcpu(dev);
|
||||
if (pc == NULL)
|
||||
return (ENXIO);
|
||||
|
||||
memset(cf, CPUFREQ_VAL_UNKNOWN, sizeof(*cf));
|
||||
cf->dev = dev;
|
||||
if ((ret = cpu_est_clockrate(pc->pc_cpuid, &rate)))
|
||||
return (ret);
|
||||
cf->freq = rate / 1000000;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
hwpstate_get_pstate(device_t dev, struct cf_setting *cf)
|
||||
{
|
||||
struct hwpstate_softc *sc;
|
||||
struct hwpstate_setting set;
|
||||
uint64_t msr;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
msr = rdmsr(MSR_AMD_10H_11H_STATUS);
|
||||
if (msr >= sc->cfnum)
|
||||
return (EINVAL);
|
||||
set = sc->hwpstate_settings[msr];
|
||||
|
||||
cf->freq = set.freq;
|
||||
cf->volts = set.volts;
|
||||
cf->power = set.power;
|
||||
cf->lat = set.lat;
|
||||
cf->dev = dev;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
hwpstate_get(device_t dev, struct cf_setting *cf)
|
||||
{
|
||||
struct hwpstate_softc *sc;
|
||||
struct hwpstate_setting set;
|
||||
struct pcpu *pc;
|
||||
uint64_t msr;
|
||||
uint64_t rate;
|
||||
int ret;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
if (cf == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
if ((sc->flags & HWPFL_USE_CPPC) != 0) {
|
||||
pc = cpu_get_pcpu(dev);
|
||||
if (pc == NULL)
|
||||
return (ENXIO);
|
||||
|
||||
memset(cf, CPUFREQ_VAL_UNKNOWN, sizeof(*cf));
|
||||
cf->dev = dev;
|
||||
if ((ret = cpu_est_clockrate(pc->pc_cpuid, &rate)))
|
||||
return (ret);
|
||||
cf->freq = rate / 1000000;
|
||||
} else {
|
||||
msr = rdmsr(MSR_AMD_10H_11H_STATUS);
|
||||
if (msr >= sc->cfnum)
|
||||
return (EINVAL);
|
||||
set = sc->hwpstate_settings[msr];
|
||||
|
||||
cf->freq = set.freq;
|
||||
cf->volts = set.volts;
|
||||
cf->power = set.power;
|
||||
cf->lat = set.lat;
|
||||
cf->dev = dev;
|
||||
}
|
||||
|
||||
return (0);
|
||||
return (sc->cpufreq_methods->get(dev, cf));
|
||||
}
|
||||
|
||||
static int
|
||||
hwpstate_settings(device_t dev, struct cf_setting *sets, int *count)
|
||||
hwpstate_settings_cppc(device_t dev __unused, struct cf_setting *sets __unused,
|
||||
int *count __unused)
|
||||
{
|
||||
return (EOPNOTSUPP);
|
||||
}
|
||||
|
||||
static int
|
||||
hwpstate_settings_pstate(device_t dev, struct cf_setting *sets, int *count)
|
||||
{
|
||||
struct hwpstate_softc *sc;
|
||||
struct hwpstate_setting set;
|
||||
struct hwpstate_softc *sc;
|
||||
int i;
|
||||
|
||||
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++) {
|
||||
@@ -709,20 +742,39 @@ hwpstate_settings(device_t dev, struct cf_setting *sets, int *count)
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
hwpstate_settings(device_t dev, struct cf_setting *sets, int *count)
|
||||
{
|
||||
struct hwpstate_softc *sc;
|
||||
|
||||
if (sets == NULL || count == NULL)
|
||||
return (EINVAL);
|
||||
sc = device_get_softc(dev);
|
||||
return (sc->cpufreq_methods->settings(dev, sets, count));
|
||||
}
|
||||
|
||||
static int
|
||||
hwpstate_type_cppc(device_t dev, int *type)
|
||||
{
|
||||
*type |= CPUFREQ_TYPE_ABSOLUTE | CPUFREQ_FLAG_INFO_ONLY |
|
||||
CPUFREQ_FLAG_UNCACHED;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
hwpstate_type_pstate(device_t dev, int *type)
|
||||
{
|
||||
*type = CPUFREQ_TYPE_ABSOLUTE;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
hwpstate_type(device_t dev, int *type)
|
||||
{
|
||||
struct hwpstate_softc *sc;
|
||||
|
||||
if (type == NULL)
|
||||
return (EINVAL);
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
*type = CPUFREQ_TYPE_ABSOLUTE;
|
||||
*type |= (sc->flags & HWPFL_USE_CPPC) != 0 ?
|
||||
CPUFREQ_FLAG_INFO_ONLY | CPUFREQ_FLAG_UNCACHED :
|
||||
0;
|
||||
return (0);
|
||||
return (sc->cpufreq_methods->type(dev, type));
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -909,34 +961,14 @@ enable_cppc(struct hwpstate_softc *sc)
|
||||
}
|
||||
|
||||
static int
|
||||
hwpstate_probe(device_t dev)
|
||||
hwpstate_probe_pstate(device_t dev)
|
||||
{
|
||||
struct hwpstate_softc *sc;
|
||||
device_t perf_dev;
|
||||
uint64_t msr;
|
||||
int error, type;
|
||||
uint64_t msr;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
if (hwpstate_amd_cppc_enable &&
|
||||
(amd_extended_feature_extensions & AMDFEID_CPPC)) {
|
||||
sc->flags |= HWPFL_USE_CPPC;
|
||||
device_set_desc(dev,
|
||||
"AMD Collaborative Processor Performance Control (CPPC)");
|
||||
} else {
|
||||
/*
|
||||
* No CPPC support. Only keep hwpstate0, it goes well with
|
||||
* acpi_throttle.
|
||||
*/
|
||||
if (device_get_unit(dev) != 0)
|
||||
return (ENXIO);
|
||||
device_set_desc(dev, "Cool`n'Quiet 2.0");
|
||||
}
|
||||
|
||||
sc->dev = dev;
|
||||
if ((sc->flags & HWPFL_USE_CPPC) != 0)
|
||||
return (0);
|
||||
|
||||
/*
|
||||
* Check if acpi_perf has INFO only flag.
|
||||
*/
|
||||
@@ -984,10 +1016,49 @@ hwpstate_probe(device_t dev)
|
||||
*/
|
||||
if (error)
|
||||
error = hwpstate_get_info_from_msr(dev);
|
||||
if (error)
|
||||
return (error);
|
||||
return (error);
|
||||
}
|
||||
|
||||
return (0);
|
||||
static const struct hwpstate_cpufreq_methods cppc_methods = {
|
||||
.get = hwpstate_get_cppc,
|
||||
.set = hwpstate_set_cppc,
|
||||
.settings = hwpstate_settings_cppc,
|
||||
.type = hwpstate_type_cppc };
|
||||
|
||||
static const struct hwpstate_cpufreq_methods pstate_methods = {
|
||||
.get = hwpstate_get_pstate,
|
||||
.set = hwpstate_set_pstate,
|
||||
.settings = hwpstate_settings_pstate,
|
||||
.type = hwpstate_type_pstate };
|
||||
|
||||
static int
|
||||
hwpstate_probe(device_t dev)
|
||||
{
|
||||
struct hwpstate_softc *sc;
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
if (hwpstate_amd_cppc_enable &&
|
||||
(amd_extended_feature_extensions & AMDFEID_CPPC)) {
|
||||
sc->flags |= HWPFL_USE_CPPC;
|
||||
device_set_desc(dev,
|
||||
"AMD Collaborative Processor Performance Control (CPPC)");
|
||||
} else {
|
||||
/*
|
||||
* No CPPC support. Only keep hwpstate0, it goes well with
|
||||
* acpi_throttle.
|
||||
*/
|
||||
if (device_get_unit(dev) != 0)
|
||||
return (ENXIO);
|
||||
device_set_desc(dev, "Cool`n'Quiet 2.0");
|
||||
}
|
||||
|
||||
sc->dev = dev;
|
||||
if ((sc->flags & HWPFL_USE_CPPC) != 0) {
|
||||
sc->cpufreq_methods = &cppc_methods;
|
||||
return (0);
|
||||
}
|
||||
sc->cpufreq_methods = &pstate_methods;
|
||||
return (hwpstate_probe_pstate(dev));
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -1037,8 +1108,8 @@ hwpstate_attach(device_t dev)
|
||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
|
||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
|
||||
"desired_performance",
|
||||
CTLTYPE_UINT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
|
||||
dev, AMD_CPPC_REQUEST_DES_PERF_BITS,
|
||||
CTLTYPE_UINT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, dev,
|
||||
AMD_CPPC_REQUEST_DES_PERF_BITS,
|
||||
sysctl_cppc_request_field_handler, "IU",
|
||||
"Desired performance level (from 0 to 255; "
|
||||
"0 enables autonomous mode, otherwise value should be "
|
||||
|
||||
Reference in new issue
Block a user