Skip to content

Commit

Permalink
platform/x86/intel-uncore-freq: Provide cluster level control
Browse files Browse the repository at this point in the history
The new generation of CPUs have granular control at a cluster level.
Each package/die can have multiple power domains, which further can
have multiple clusters. The TPMI interface allows control at cluster
level.

Use the updated uncore sysfs feature to expose controls at cluster
level. At each cluster level there is a control for maximum and minimum
uncore frequency. Also present current uncore frequency at a cluster
level.

If user already has set the maximum/minimum limits for the whole package,
then per cluster limits can be only add more restrictive limits. For
example user has set package/die maximum frequency to 2 GHz, then per
cluster limit can't exceed that. It can further restrict them for example
to 1 GHz.

Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
  • Loading branch information
spandruvada committed Mar 28, 2023
1 parent 0d66ea4 commit cb5c234
Showing 1 changed file with 104 additions and 26 deletions.
130 changes: 104 additions & 26 deletions drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c
Expand Up @@ -44,6 +44,7 @@ struct tpmi_uncore_struct;

/* Information for each cluster */
struct tpmi_uncore_cluster_info {
bool root_domain;
void __iomem *cluster_base;
struct uncore_data uncore_data;
struct tpmi_uncore_struct *uncore_root;
Expand All @@ -60,12 +61,15 @@ struct tpmi_uncore_power_domain_info {
/* Information for all power domains in a package */
struct tpmi_uncore_struct {
int power_domain_count;
int max_ratio;
int min_ratio;
struct tpmi_uncore_power_domain_info *pd_info;
struct tpmi_uncore_cluster_info root_cluster;
};

#define UNCORE_GENMASK_MIN_RATIO GENMASK_ULL(21, 15)
#define UNCORE_GENMASK_MAX_RATIO GENMASK_ULL(14, 8)
#define UNCORE_GENMASK_CURRENT_RATIO GENMASK_ULL(6, 0)

/* Helper function to read MMIO offset for max/min control frequency */
static void read_control_freq(struct tpmi_uncore_cluster_info *cluster_info,
Expand All @@ -85,32 +89,37 @@ static int uncore_read_control_freq(struct uncore_data *data, unsigned int *min,
unsigned int *max)
{
struct tpmi_uncore_cluster_info *cluster_info;
struct tpmi_uncore_struct *uncore_root;
int i, _min = 0, _max = 0;

cluster_info = container_of(data, struct tpmi_uncore_cluster_info, uncore_data);
uncore_root = cluster_info->uncore_root;

*min = UNCORE_MAX_RATIO * UNCORE_FREQ_KHZ_MULTIPLIER;
*max = 0;
if (cluster_info->root_domain) {
struct tpmi_uncore_struct *uncore_root = cluster_info->uncore_root;
int i, _min = 0, _max = 0;

/*
* Get the max/min by looking at each cluster. Get the lowest
* min and highest max.
*/
for (i = 0; i < uncore_root->power_domain_count; ++i) {
int j;
*min = UNCORE_MAX_RATIO * UNCORE_FREQ_KHZ_MULTIPLIER;
*max = 0;

for (j = 0; j < uncore_root->pd_info[i].cluster_count; ++j) {
read_control_freq(&uncore_root->pd_info[i].cluster_infos[j],
&_min, &_max);
if (*min > _min)
*min = _min;
if (*max < _max)
*max = _max;
/*
* Get the max/min by looking at each cluster. Get the lowest
* min and highest max.
*/
for (i = 0; i < uncore_root->power_domain_count; ++i) {
int j;

for (j = 0; j < uncore_root->pd_info[i].cluster_count; ++j) {
read_control_freq(&uncore_root->pd_info[i].cluster_infos[j],
&_min, &_max);
if (*min > _min)
*min = _min;
if (*max < _max)
*max = _max;
}
}
return 0;
}

read_control_freq(cluster_info, min, max);

return 0;
}

Expand Down Expand Up @@ -139,7 +148,6 @@ static int uncore_write_control_freq(struct uncore_data *data, unsigned int inpu
{
struct tpmi_uncore_cluster_info *cluster_info;
struct tpmi_uncore_struct *uncore_root;
int i;

input /= UNCORE_FREQ_KHZ_MULTIPLIER;
if (!input || input > UNCORE_MAX_RATIO)
Expand All @@ -149,21 +157,76 @@ static int uncore_write_control_freq(struct uncore_data *data, unsigned int inpu
uncore_root = cluster_info->uncore_root;

/* Update each cluster in a package */
for (i = 0; i < uncore_root->power_domain_count; ++i) {
int j;
if (cluster_info->root_domain) {
struct tpmi_uncore_struct *uncore_root = cluster_info->uncore_root;
int i;

/*
* Get the max/min by looking at each cluster. Get the lowest
* min and highest max.
*/
for (i = 0; i < uncore_root->power_domain_count; ++i) {
int j;

for (j = 0; j < uncore_root->pd_info[i].cluster_count; ++j)
write_control_freq(&uncore_root->pd_info[i].cluster_infos[j],
input, min_max);
for (j = 0; j < uncore_root->pd_info[i].cluster_count; ++j)
write_control_freq(&uncore_root->pd_info[i].cluster_infos[j],
input, min_max);
}

if (min_max)
uncore_root->max_ratio = input;
else
uncore_root->min_ratio = input;

return 0;
}

if (min_max && uncore_root->max_ratio && uncore_root->max_ratio < input)
return -EINVAL;

if (!min_max && uncore_root->min_ratio && uncore_root->min_ratio > input)
return -EINVAL;

write_control_freq(cluster_info, input, min_max);

return 0;
}

/* Callback for sysfs read for the current uncore frequency. Called under mutex locks */
static int uncore_read_freq(struct uncore_data *data, unsigned int *freq)
{
return -ENODATA;
struct tpmi_uncore_cluster_info *cluster_info;
u64 status;

cluster_info = container_of(data, struct tpmi_uncore_cluster_info, uncore_data);
if (cluster_info->root_domain)
return -ENODATA;

status = readq((u8 __iomem *)cluster_info->cluster_base + UNCORE_STATUS_INDEX);
*freq = FIELD_GET(UNCORE_GENMASK_CURRENT_RATIO, status) * UNCORE_FREQ_KHZ_MULTIPLIER;

return 0;
}

static void remove_cluster_entries(struct tpmi_uncore_struct *tpmi_uncore)
{
int i;

for (i = 0; i < tpmi_uncore->power_domain_count; ++i) {
struct tpmi_uncore_power_domain_info *pd_info;
int j;

pd_info = &tpmi_uncore->pd_info[i];
if (!pd_info->uncore_base)
continue;

for (j = 0; j < pd_info->cluster_count; ++j) {
struct tpmi_uncore_cluster_info *cluster_info;

cluster_info = &pd_info->cluster_infos[j];
uncore_freq_remove_die_entry(&cluster_info->uncore_data);
}
}
}

#define UNCORE_GENMASK_VERSION GENMASK_ULL(7, 0)
Expand Down Expand Up @@ -283,22 +346,36 @@ static int tpmi_uncore_init(struct auxiliary_device *auxdev)
cluster_info->uncore_data.package_id = pkg;
/* There are no dies like Cascade Lake */
cluster_info->uncore_data.die_id = 0;
cluster_info->uncore_data.domain_id = i;
cluster_info->uncore_data.cluster_id = j;

cluster_info->uncore_root = tpmi_uncore;

ret = uncore_freq_add_entry(&cluster_info->uncore_data, 0);
if (ret) {
cluster_info->cluster_base = NULL;
goto remove_clusters;
}
/* Point to next cluster offset */
cluster_offset >>= UNCORE_MAX_CLUSTER_PER_DOMAIN;
}
}

auxiliary_set_drvdata(auxdev, tpmi_uncore);

tpmi_uncore->root_cluster.root_domain = true;
tpmi_uncore->root_cluster.uncore_root = tpmi_uncore;

tpmi_uncore->root_cluster.uncore_data.package_id = pkg;
tpmi_uncore->root_cluster.uncore_data.domain_id = UNCORE_DOMAIN_ID_INVALID;
ret = uncore_freq_add_entry(&tpmi_uncore->root_cluster.uncore_data, 0);
if (ret)
goto err_rem_common;
goto remove_clusters;

return 0;

remove_clusters:
remove_cluster_entries(tpmi_uncore);
err_rem_common:
uncore_freq_common_exit();

Expand All @@ -310,6 +387,7 @@ static int tpmi_uncore_remove(struct auxiliary_device *auxdev)
struct tpmi_uncore_struct *tpmi_uncore = auxiliary_get_drvdata(auxdev);

uncore_freq_remove_die_entry(&tpmi_uncore->root_cluster.uncore_data);
remove_cluster_entries(tpmi_uncore);

uncore_freq_common_exit();

Expand Down

0 comments on commit cb5c234

Please sign in to comment.