Unverified Commit 1fe70419 authored by openeuler-ci-bot's avatar openeuler-ci-bot Committed by Gitee
Browse files

!4387 intel: backport uncore freq control tpmi support for BHS platform

Merge Pull Request from: @jiayingbao 
 
Implement support of uncore frequency control via TPMI (Topology Aware
Register and PM Capsule Interface). This driver provides the similar
functionality as the current uncore frequency driver using MSRs.

The scope of the uncore MSRs is package/die. But new generation of CPUs
have more 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.

The primary use case for uncore sysfs is to set maximum and minimum
uncore frequency to reduce power consumption or latency. The current
uncore sysfs control is per package/die. This is enough for the majority
of users as workload will move to different power domains as it moves
between different CPUs.

The TPMI documentation can be downloaded from:
https://github.com/intel/tpmi_power_management

Test:
uncore freq sysfs work as expected

config:
+CONFIG_INTEL_UNCORE_FREQ_CONTROL=m
+CONFIG_INTEL_UNCORE_FREQ_CONTROL_TPMI=m 
 
Link:https://gitee.com/openeuler/kernel/pulls/4387

 

Reviewed-by: default avatarLiu Chao <liuchao173@huawei.com>
Reviewed-by: default avatarJason Zeng <jason.zeng@intel.com>
Signed-off-by: default avatarJialin Zhang <zhangjialin11@huawei.com>
parents 17c4a028 79372d16
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -6638,7 +6638,8 @@ CONFIG_INTEL_SPEED_SELECT_INTERFACE=m
# end of Intel Speed Select Technology interface support

CONFIG_INTEL_TURBO_MAX_3=y
# CONFIG_INTEL_UNCORE_FREQ_CONTROL is not set
CONFIG_INTEL_UNCORE_FREQ_CONTROL=m
CONFIG_INTEL_UNCORE_FREQ_CONTROL_TPMI=m
CONFIG_INTEL_VSEC=m
CONFIG_INTEL_PMC_CORE=m
CONFIG_INTEL_PMT_CLASS=m
+3 −0
Original line number Diff line number Diff line
@@ -1318,9 +1318,12 @@ config INTEL_TURBO_MAX_3
	  This driver is only required when the system is not using Hardware
	  P-States (HWP). In HWP mode, priority can be read from ACPI tables.

config INTEL_UNCORE_FREQ_CONTROL_TPMI
	tristate
config INTEL_UNCORE_FREQ_CONTROL
	tristate "Intel Uncore frequency control driver"
	depends on X86_64
	select INTEL_UNCORE_FREQ_CONTROL_TPMI if INTEL_TPMI
	help
	  This driver allows control of uncore frequency limits on
	  supported server platforms.
+2 −0
Original line number Diff line number Diff line
@@ -138,6 +138,8 @@ obj-$(CONFIG_INTEL_SMARTCONNECT) += intel-smartconnect.o
obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE)	+= intel_speed_select_if/
obj-$(CONFIG_INTEL_TURBO_MAX_3)			+= intel_turbo_max_3.o
obj-$(CONFIG_INTEL_UNCORE_FREQ_CONTROL)		+= intel-uncore-frequency.o
obj-$(CONFIG_INTEL_UNCORE_FREQ_CONTROL)		+= intel-uncore-frequency-common.o
obj-$(CONFIG_INTEL_UNCORE_FREQ_CONTROL_TPMI)	+= intel-uncore-frequency-tpmi.o

# Intel PMIC / PMC / P-Unit devices
obj-$(CONFIG_INTEL_BXTWC_PMIC_TMU)	+= intel_bxtwc_tmu.o
+307 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/*
 * Intel Uncore Frequency Control: Common code implementation
 * Copyright (c) 2022, Intel Corporation.
 * All rights reserved.
 *
 */
#include <linux/cpu.h>
#include <linux/module.h>
#include "intel-uncore-frequency-common.h"

/* Mutex to control all mutual exclusions */
static DEFINE_MUTEX(uncore_lock);
/* Root of the all uncore sysfs kobjs */
static struct kobject *uncore_root_kobj;
/* uncore instance count */
static int uncore_instance_count;

static DEFINE_IDA(intel_uncore_ida);

/* callbacks for actual HW read/write */
static int (*uncore_read)(struct uncore_data *data, unsigned int *min, unsigned int *max);
static int (*uncore_write)(struct uncore_data *data, unsigned int input, unsigned int min_max);
static int (*uncore_read_freq)(struct uncore_data *data, unsigned int *freq);

static ssize_t show_domain_id(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct uncore_data *data = container_of(attr, struct uncore_data, domain_id_dev_attr);

	return sprintf(buf, "%u\n", data->domain_id);
}

static ssize_t show_fabric_cluster_id(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct uncore_data *data = container_of(attr, struct uncore_data, fabric_cluster_id_dev_attr);

	return sprintf(buf, "%u\n", data->cluster_id);
}

static ssize_t show_package_id(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct uncore_data *data = container_of(attr, struct uncore_data, package_id_dev_attr);

	return sprintf(buf, "%u\n", data->package_id);
}

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(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);
}

static ssize_t store_min_max_freq_khz(struct uncore_data *data,
				      const char *buf, ssize_t count,
				      int min_max)
{
	unsigned int input;
	int ret;

	if (kstrtouint(buf, 10, &input))
		return -EINVAL;

	mutex_lock(&uncore_lock);
	ret = uncore_write(data, input, min_max);
	mutex_unlock(&uncore_lock);

	if (ret)
		return ret;

	return count;
}

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_freq(data, &freq);
	mutex_unlock(&uncore_lock);
	if (ret)
		return ret;

	return sprintf(buf, "%u\n", freq);
}

#define store_uncore_min_max(name, min_max)				\
	static ssize_t store_##name(struct device *dev,		\
				     struct device_attribute *attr,	\
				     const char *buf, size_t count)	\
	{								\
		struct uncore_data *data = container_of(attr, struct uncore_data, name##_dev_attr);\
									\
		return store_min_max_freq_khz(data, buf, count,	\
					      min_max);		\
	}

#define show_uncore_min_max(name, min_max)				\
	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_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)\
	{                                                               \
		struct uncore_data *data = container_of(attr, struct uncore_data,\
							  member_name##_dev_attr);\
									\
		return scnprintf(buf, PAGE_SIZE, "%u\n",		\
				 data->member_name);			\
	}								\

show_uncore_data(initial_min_freq_khz);
show_uncore_data(initial_max_freq_khz);

#define init_attribute_rw(_name)					\
	do {								\
		sysfs_attr_init(&data->_name##_dev_attr.attr);	\
		data->_name##_dev_attr.show = show_##_name;		\
		data->_name##_dev_attr.store = store_##_name;		\
		data->_name##_dev_attr.attr.name = #_name;		\
		data->_name##_dev_attr.attr.mode = 0644;		\
	} while (0)

#define init_attribute_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 = 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, freq, index = 0;

	init_attribute_rw(max_freq_khz);
	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);

	if (data->domain_id != UNCORE_DOMAIN_ID_INVALID) {
		init_attribute_root_ro(domain_id);
		data->uncore_attrs[index++] = &data->domain_id_dev_attr.attr;
		init_attribute_root_ro(fabric_cluster_id);
		data->uncore_attrs[index++] = &data->fabric_cluster_id_dev_attr.attr;
		init_attribute_root_ro(package_id);
		data->uncore_attrs[index++] = &data->package_id_dev_attr.attr;
	}

	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;

	ret = uncore_read_freq(data, &freq);
	if (!ret)
		data->uncore_attrs[index++] = &data->current_freq_khz_dev_attr.attr;

	data->uncore_attrs[index] = NULL;

	data->uncore_attr_group.name = name;
	data->uncore_attr_group.attrs = data->uncore_attrs;
	ret = sysfs_create_group(uncore_root_kobj, &data->uncore_attr_group);

	return ret;
}

static void delete_attr_group(struct uncore_data *data, char *name)
{
	sysfs_remove_group(uncore_root_kobj, &data->uncore_attr_group);
}

int uncore_freq_add_entry(struct uncore_data *data, int cpu)
{
	int ret = 0;

	mutex_lock(&uncore_lock);
	if (data->valid) {
		/* control cpu changed */
		data->control_cpu = cpu;
		goto uncore_unlock;
	}

	if (data->domain_id != UNCORE_DOMAIN_ID_INVALID) {
		ret = ida_alloc(&intel_uncore_ida, GFP_KERNEL);
		if (ret < 0)
			goto uncore_unlock;

		data->instance_id = ret;
		sprintf(data->name, "uncore%02d", ret);
	} else {
		sprintf(data->name, "package_%02d_die_%02d", data->package_id, data->die_id);
	}

	uncore_read(data, &data->initial_min_freq_khz, &data->initial_max_freq_khz);

	ret = create_attr_group(data, data->name);
	if (ret) {
		if (data->domain_id != UNCORE_DOMAIN_ID_INVALID)
			ida_free(&intel_uncore_ida, data->instance_id);
	} else {
		data->control_cpu = cpu;
		data->valid = true;
	}

uncore_unlock:
	mutex_unlock(&uncore_lock);

	return ret;
}
EXPORT_SYMBOL_NS_GPL(uncore_freq_add_entry, INTEL_UNCORE_FREQUENCY);

void uncore_freq_remove_die_entry(struct uncore_data *data)
{
	mutex_lock(&uncore_lock);
	delete_attr_group(data, data->name);
	data->control_cpu = -1;
	data->valid = false;
	if (data->domain_id != UNCORE_DOMAIN_ID_INVALID)
		ida_free(&intel_uncore_ida, data->instance_id);

	mutex_unlock(&uncore_lock);
}
EXPORT_SYMBOL_NS_GPL(uncore_freq_remove_die_entry, INTEL_UNCORE_FREQUENCY);

int uncore_freq_common_init(int (*read_control_freq)(struct uncore_data *data, unsigned int *min, unsigned int *max),
			     int (*write_control_freq)(struct uncore_data *data, unsigned int input, unsigned int set_max),
			     int (*read_freq)(struct uncore_data *data, unsigned int *freq))
{
	mutex_lock(&uncore_lock);

	uncore_read = read_control_freq;
	uncore_write = write_control_freq;
	uncore_read_freq = read_freq;

	if (!uncore_root_kobj)
		uncore_root_kobj = kobject_create_and_add("intel_uncore_frequency",
							    &cpu_subsys.dev_root->kobj);
	if (uncore_root_kobj)
		++uncore_instance_count;
	mutex_unlock(&uncore_lock);

	return uncore_root_kobj ? 0 : -ENOMEM;
}
EXPORT_SYMBOL_NS_GPL(uncore_freq_common_init, INTEL_UNCORE_FREQUENCY);

void uncore_freq_common_exit(void)
{
	mutex_lock(&uncore_lock);
	--uncore_instance_count;
	if (!uncore_instance_count) {
		kobject_put(uncore_root_kobj);
		uncore_root_kobj = NULL;
	}
	mutex_unlock(&uncore_lock);
}
EXPORT_SYMBOL_NS_GPL(uncore_freq_common_exit, INTEL_UNCORE_FREQUENCY);


MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Intel Uncore Frequency Common Module");
+76 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Intel Uncore Frequency Control: Common defines and prototypes
 * Copyright (c) 2022, Intel Corporation.
 * All rights reserved.
 *
 */

#ifndef __INTEL_UNCORE_FREQ_COMMON_H
#define __INTEL_UNCORE_FREQ_COMMON_H

#include <linux/device.h>

/**
 * struct uncore_data - Encapsulate all uncore data
 * @stored_uncore_data: Last user changed MSR 620 value, which will be restored
 *			on system resume.
 * @initial_min_freq_khz: Sampled minimum uncore frequency at driver init
 * @initial_max_freq_khz: Sampled maximum uncore frequency at driver init
 * @control_cpu:	Designated CPU for a die to read/write
 * @valid:		Mark the data valid/invalid
 * @package_id:	Package id for this instance
 * @die_id:		Die id for this instance
 * @domain_id:		Power domain id for this instance
 * @cluster_id:		cluster id in a domain
 * @instance_id:	Unique instance id to append to directory name
 * @name:		Sysfs entry name for this instance
 * @uncore_attr_group:	Attribute group storage
 * @max_freq_khz_dev_attr: Storage for device attribute max_freq_khz
 * @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
 * @domain_id_dev_attr: Storage for device attribute domain_id
 * @fabric_cluster_id_dev_attr: Storage for device attribute fabric_cluster_id
 * @package_id_dev_attr: Storage for device attribute package_id
 * @uncore_attrs:	Attribute storage for group creation
 *
 * This structure is used to encapsulate all data related to uncore sysfs
 * settings for a die/package.
 */
struct uncore_data {
	u64 stored_uncore_data;
	u32 initial_min_freq_khz;
	u32 initial_max_freq_khz;
	int control_cpu;
	bool valid;
	int package_id;
	int die_id;
	int domain_id;
	int cluster_id;
	int instance_id;
	char name[32];

	struct attribute_group uncore_attr_group;
	struct device_attribute max_freq_khz_dev_attr;
	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 device_attribute current_freq_khz_dev_attr;
	struct device_attribute domain_id_dev_attr;
	struct device_attribute fabric_cluster_id_dev_attr;
	struct device_attribute package_id_dev_attr;
	struct attribute *uncore_attrs[9];
};

#define UNCORE_DOMAIN_ID_INVALID	-1

int uncore_freq_common_init(int (*read_control_freq)(struct uncore_data *data, unsigned int *min, unsigned int *max),
			     int (*write_control_freq)(struct uncore_data *data, unsigned int input, unsigned int min_max),
			     int (*uncore_read_freq)(struct uncore_data *data, unsigned int *freq));
void uncore_freq_common_exit(void);
int uncore_freq_add_entry(struct uncore_data *data, int cpu);
void uncore_freq_remove_die_entry(struct uncore_data *data);

#endif
Loading