[PATCH 3/4] platform/x86/intel/uncore-freq: Display uncore current frequency

From: Srinivas Pandruvada
Date: Thu Feb 03 2022 - 19:03:49 EST


From: Srinivas Pandruvada <srinivas.pandruvada@xxxxxxxxx>

Add a new sysfs attribute "current_freq_khz" to display current uncore
frequency. This value is read from MSR 0x621.

Root user permission is required to read uncore current frequency.

Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@xxxxxxxxx>
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@xxxxxxxxx>
---
.../intel/uncore-frequency/uncore-frequency.c | 71 ++++++++++++++++---
1 file changed, 62 insertions(+), 9 deletions(-)

diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c
index 35b00608a81d..f5e980163911 100644
--- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c
+++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c
@@ -22,6 +22,7 @@
#include <asm/intel-family.h>

#define MSR_UNCORE_RATIO_LIMIT 0x620
+#define MSR_UNCORE_PERF_STATUS 0x621
#define UNCORE_FREQ_KHZ_MULTIPLIER 100000

/**
@@ -40,6 +41,7 @@
* @mix_freq_khz_dev_attr: Storage for device attribute min_freq_khz
* @initial_max_freq_khz_dev_attr: Storage for device attribute initial_max_freq_khz
* @initial_min_freq_khz_dev_attr: Storage for device attribute initial_min_freq_khz
+ * @current_freq_khz_dev_attr: Storage for device attribute current_freq_khz
* @uncore_attrs: Attribute storage for group creation
*
* This structure is used to encapsulate all data related to uncore sysfs
@@ -60,7 +62,8 @@ struct uncore_data {
struct device_attribute min_freq_khz_dev_attr;
struct device_attribute initial_max_freq_khz_dev_attr;
struct device_attribute initial_min_freq_khz_dev_attr;
- struct attribute *uncore_attrs[5];
+ struct device_attribute current_freq_khz_dev_attr;
+ struct attribute *uncore_attrs[6];
};

/* Max instances for uncore data, one for each die */
@@ -131,22 +134,32 @@ static int uncore_write_ratio(struct uncore_data *data, unsigned int input,
return 0;
}

-static ssize_t show_min_max_freq_khz(struct uncore_data *data,
- char *buf, int min_max)
+static int uncore_read_freq(struct uncore_data *data, unsigned int *freq)
{
- unsigned int min, max;
+ u64 ratio;
+ int ret;
+
+ ret = rdmsrl_on_cpu(data->control_cpu, MSR_UNCORE_PERF_STATUS, &ratio);
+ if (ret)
+ return ret;
+
+ *freq = (ratio & 0x7F) * UNCORE_FREQ_KHZ_MULTIPLIER;
+
+ return 0;
+}
+
+static ssize_t show_perf_status_freq_khz(struct uncore_data *data, char *buf)
+{
+ unsigned int freq;
int ret;

mutex_lock(&uncore_lock);
- ret = uncore_read_ratio(data, &min, &max);
+ ret = uncore_read_freq(data, &freq);
mutex_unlock(&uncore_lock);
if (ret)
return ret;

- if (min_max)
- return sprintf(buf, "%u\n", max);
-
- return sprintf(buf, "%u\n", min);
+ return sprintf(buf, "%u\n", freq);
}

static ssize_t store_min_max_freq_khz(struct uncore_data *data,
@@ -165,6 +178,24 @@ static ssize_t store_min_max_freq_khz(struct uncore_data *data,
return count;
}

+static ssize_t show_min_max_freq_khz(struct uncore_data *data,
+ char *buf, int min_max)
+{
+ unsigned int min, max;
+ int ret;
+
+ mutex_lock(&uncore_lock);
+ ret = uncore_read_ratio(data, &min, &max);
+ mutex_unlock(&uncore_lock);
+ if (ret)
+ return ret;
+
+ if (min_max)
+ return sprintf(buf, "%u\n", max);
+
+ return sprintf(buf, "%u\n", min);
+}
+
#define store_uncore_min_max(name, min_max) \
static ssize_t store_##name(struct device *dev, \
struct device_attribute *attr, \
@@ -185,12 +216,23 @@ static ssize_t store_min_max_freq_khz(struct uncore_data *data,
return show_min_max_freq_khz(data, buf, min_max); \
}

+#define show_uncore_perf_status(name) \
+ static ssize_t show_##name(struct device *dev, \
+ struct device_attribute *attr, char *buf)\
+ { \
+ struct uncore_data *data = container_of(attr, struct uncore_data, name##_dev_attr);\
+ \
+ return show_perf_status_freq_khz(data, buf); \
+ }
+
store_uncore_min_max(min_freq_khz, 0);
store_uncore_min_max(max_freq_khz, 1);

show_uncore_min_max(min_freq_khz, 0);
show_uncore_min_max(max_freq_khz, 1);

+show_uncore_perf_status(current_freq_khz);
+
#define show_uncore_data(member_name) \
static ssize_t show_##member_name(struct device *dev, \
struct device_attribute *attr, char *buf)\
@@ -223,6 +265,15 @@ show_uncore_data(initial_max_freq_khz);
data->_name##_dev_attr.attr.mode = 0444; \
} while (0)

+#define init_attribute_root_ro(_name) \
+ do { \
+ sysfs_attr_init(&data->_name##_dev_attr.attr); \
+ data->_name##_dev_attr.show = show_##_name; \
+ data->_name##_dev_attr.store = NULL; \
+ data->_name##_dev_attr.attr.name = #_name; \
+ data->_name##_dev_attr.attr.mode = 0400; \
+ } while (0)
+
static int create_attr_group(struct uncore_data *data, char *name)
{
int ret, index = 0;
@@ -231,11 +282,13 @@ static int create_attr_group(struct uncore_data *data, char *name)
init_attribute_rw(min_freq_khz);
init_attribute_ro(initial_min_freq_khz);
init_attribute_ro(initial_max_freq_khz);
+ init_attribute_root_ro(current_freq_khz);

data->uncore_attrs[index++] = &data->max_freq_khz_dev_attr.attr;
data->uncore_attrs[index++] = &data->min_freq_khz_dev_attr.attr;
data->uncore_attrs[index++] = &data->initial_min_freq_khz_dev_attr.attr;
data->uncore_attrs[index++] = &data->initial_max_freq_khz_dev_attr.attr;
+ data->uncore_attrs[index++] = &data->current_freq_khz_dev_attr.attr;
data->uncore_attrs[index] = NULL;

data->uncore_attr_group.name = name;
--
2.31.1