diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c index cad7b79bedbb..7d0a67f8b517 100644 --- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c +++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c @@ -44,6 +44,7 @@ struct tpmi_uncore_struct; /* Information for each cluster */ struct tpmi_uncore_cluster_info { + bool root_domain; u8 __iomem *cluster_base; struct uncore_data uncore_data; struct tpmi_uncore_struct *uncore_root; @@ -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, @@ -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; } @@ -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) @@ -149,21 +157,72 @@ 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; - 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 (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); + } + + 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_VERSION_MASK GENMASK_ULL(7, 0) @@ -231,7 +290,13 @@ static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_ pd_info->uncore_base = devm_ioremap_resource(&auxdev->dev, res); if (IS_ERR(pd_info->uncore_base)) { ret = PTR_ERR(pd_info->uncore_base); - goto err_rem_common; + /* + * Set to NULL so that clean up can still remove other + * entries already created if any by + * remove_cluster_entries() + */ + pd_info->uncore_base = NULL; + goto remove_clusters; } /* Check for version and skip this resource if there is mismatch */ @@ -258,7 +323,7 @@ static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_ GFP_KERNEL); if (!pd_info->cluster_infos) { ret = -ENOMEM; - goto err_rem_common; + goto remove_clusters; } /* * Each byte in the register point to status and control @@ -282,7 +347,16 @@ static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_ 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; } @@ -290,14 +364,19 @@ static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_ 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(); @@ -309,6 +388,7 @@ static void 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(); }