Commit edf309e0 authored by Srinivas Pandruvada's avatar Srinivas Pandruvada Committed by Yingbao Jia
Browse files

platform/x86/intel-uncore-freq: tpmi: Provide cluster level control

mainline inclusion
from mainline-v6.5-rc1
commit 01c10f88
category: feature
bugzilla: https://gitee.com/openeuler/intel-kernel/issues/I8WOEG

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=01c10f88c9b7ab5767922531167f933cac32e9e9



-------------------------------------

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 fabric clusters. The TPMI interface allows control at
fabric 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.

Intel-SIG: commit 01c10f88 platform/x86/intel-uncore-freq: tpmi: Provide cluster level control.
Backport Intel-uncore-freq driver support on TPMI.

Signed-off-by: default avatarSrinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Reviewed-by: default avatarZhang Rui <rui.zhang@intel.com>
Tested-by: default avatarWendy Wang <wendy.wang@intel.com>
Link: https://lore.kernel.org/r/20230418171340.681662-4-srinivas.pandruvada@linux.intel.com


Signed-off-by: default avatarHans de Goede <hdegoede@redhat.com>
[ Yingbao Jia: amend commit log ]
Signed-off-by: default avatarYingbao Jia <yingbao.jia@intel.com>
parent 4ec7c966
Loading
Loading
Loading
Loading
+108 −28
Original line number Diff line number Diff line
@@ -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,11 +89,12 @@ 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;

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

		*min = UNCORE_MAX_RATIO * UNCORE_FREQ_KHZ_MULTIPLIER;
		*max = 0;
@@ -110,6 +115,10 @@ static int uncore_read_control_freq(struct uncore_data *data, unsigned int *min,
					*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,6 +157,10 @@ 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 */
	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;

@@ -157,13 +169,60 @@ static int uncore_write_control_freq(struct uncore_data *data, unsigned int inpu
						  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)
{
	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();
}