/* 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;
/* 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,
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;
}
{
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)
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 (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_VERSION_MASK GENMASK_ULL(7, 0)
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 */
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
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();
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();
}