File Index
Files and directories
Sign in
FILE

uncore-frequency-common.c

File preview with syntax highlighting.

linux-6.19.14/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Intel Uncore Frequency Control: Common code implementation
  4 * Copyright (c) 2022, Intel Corporation.
  5 * All rights reserved.
  6 *
  7 */
  8#include <linux/cpu.h>
  9#include <linux/module.h>
 10#include "uncore-frequency-common.h"
 11
 12/* Mutex to control all mutual exclusions */
 13static DEFINE_MUTEX(uncore_lock);
 14/* Root of the all uncore sysfs kobjs */
 15static struct kobject *uncore_root_kobj;
 16/* uncore instance count */
 17static int uncore_instance_count;
 18
 19static DEFINE_IDA(intel_uncore_ida);
 20
 21/* callbacks for actual HW read/write */
 22static int (*uncore_read)(struct uncore_data *data, unsigned int *value, enum uncore_index index);
 23static int (*uncore_write)(struct uncore_data *data, unsigned int input, enum uncore_index index);
 24
 25static ssize_t show_domain_id(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
 26{
 27	struct uncore_data *data = container_of(attr, struct uncore_data, domain_id_kobj_attr);
 28
 29	return sprintf(buf, "%u\n", data->domain_id);
 30}
 31
 32static ssize_t show_fabric_cluster_id(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
 33{
 34	struct uncore_data *data = container_of(attr, struct uncore_data, fabric_cluster_id_kobj_attr);
 35
 36	return sprintf(buf, "%u\n", data->cluster_id);
 37}
 38
 39static ssize_t show_package_id(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
 40{
 41	struct uncore_data *data = container_of(attr, struct uncore_data, package_id_kobj_attr);
 42
 43	return sprintf(buf, "%u\n", data->package_id);
 44}
 45
 46#define MAX_UNCORE_AGENT_TYPES	4
 47
 48/* The order follows AGENT_TYPE_* defines */
 49static const char *agent_name[MAX_UNCORE_AGENT_TYPES] = {"core", "cache", "memory", "io"};
 50
 51static ssize_t show_agent_types(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
 52{
 53	struct uncore_data *data = container_of(attr, struct uncore_data, agent_types_kobj_attr);
 54	unsigned long agent_mask = data->agent_type_mask;
 55	int agent, length = 0;
 56
 57	for_each_set_bit(agent, &agent_mask, MAX_UNCORE_AGENT_TYPES) {
 58		if (length)
 59			length += sysfs_emit_at(buf, length, " ");
 60
 61		length += sysfs_emit_at(buf, length, "%s", agent_name[agent]);
 62	}
 63
 64	length += sysfs_emit_at(buf, length, "\n");
 65
 66	return length;
 67}
 68
 69static ssize_t show_attr(struct uncore_data *data, char *buf, enum uncore_index index)
 70{
 71	unsigned int value;
 72	int ret;
 73
 74	mutex_lock(&uncore_lock);
 75	ret = uncore_read(data, &value, index);
 76	mutex_unlock(&uncore_lock);
 77	if (ret)
 78		return ret;
 79
 80	return sprintf(buf, "%u\n", value);
 81}
 82
 83static ssize_t store_attr(struct uncore_data *data, const char *buf, ssize_t count,
 84			  enum uncore_index index)
 85{
 86	unsigned int input = 0;
 87	int ret;
 88
 89	if (index == UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD_ENABLE) {
 90		if (kstrtobool(buf, (bool *)&input))
 91			return -EINVAL;
 92	} else {
 93		if (kstrtouint(buf, 10, &input))
 94			return -EINVAL;
 95	}
 96
 97	mutex_lock(&uncore_lock);
 98	ret = uncore_write(data, input, index);
 99	mutex_unlock(&uncore_lock);
100
101	if (ret)
102		return ret;
103
104	return count;
105}
106
107#define store_uncore_attr(name, index)					\
108	static ssize_t store_##name(struct kobject *kobj,		\
109				     struct kobj_attribute *attr,	\
110				     const char *buf, size_t count)	\
111	{								\
112		struct uncore_data *data = container_of(attr, struct uncore_data, name##_kobj_attr);\
113									\
114		return store_attr(data, buf, count, index);		\
115	}
116
117#define show_uncore_attr(name, index)					\
118	static ssize_t show_##name(struct kobject *kobj,		\
119				    struct kobj_attribute *attr, char *buf)\
120	{                                                               \
121		struct uncore_data *data = container_of(attr, struct uncore_data, name##_kobj_attr);\
122									\
123		return show_attr(data, buf, index);			\
124	}
125
126store_uncore_attr(min_freq_khz, UNCORE_INDEX_MIN_FREQ);
127store_uncore_attr(max_freq_khz, UNCORE_INDEX_MAX_FREQ);
128
129show_uncore_attr(min_freq_khz, UNCORE_INDEX_MIN_FREQ);
130show_uncore_attr(max_freq_khz, UNCORE_INDEX_MAX_FREQ);
131
132show_uncore_attr(current_freq_khz, UNCORE_INDEX_CURRENT_FREQ);
133
134store_uncore_attr(elc_low_threshold_percent, UNCORE_INDEX_EFF_LAT_CTRL_LOW_THRESHOLD);
135store_uncore_attr(elc_high_threshold_percent, UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD);
136store_uncore_attr(elc_high_threshold_enable,
137		  UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD_ENABLE);
138store_uncore_attr(elc_floor_freq_khz, UNCORE_INDEX_EFF_LAT_CTRL_FREQ);
139
140show_uncore_attr(elc_low_threshold_percent, UNCORE_INDEX_EFF_LAT_CTRL_LOW_THRESHOLD);
141show_uncore_attr(elc_high_threshold_percent, UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD);
142show_uncore_attr(elc_high_threshold_enable,
143		 UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD_ENABLE);
144show_uncore_attr(elc_floor_freq_khz, UNCORE_INDEX_EFF_LAT_CTRL_FREQ);
145
146show_uncore_attr(die_id, UNCORE_INDEX_DIE_ID);
147
148#define show_uncore_data(member_name)					\
149	static ssize_t show_##member_name(struct kobject *kobj,	\
150					   struct kobj_attribute *attr, char *buf)\
151	{                                                               \
152		struct uncore_data *data = container_of(attr, struct uncore_data,\
153							  member_name##_kobj_attr);\
154									\
155		return sysfs_emit(buf, "%u\n",				\
156				 data->member_name);			\
157	}								\
158
159show_uncore_data(initial_min_freq_khz);
160show_uncore_data(initial_max_freq_khz);
161
162#define init_attribute_rw(_name)					\
163	do {								\
164		sysfs_attr_init(&data->_name##_kobj_attr.attr);	\
165		data->_name##_kobj_attr.show = show_##_name;		\
166		data->_name##_kobj_attr.store = store_##_name;		\
167		data->_name##_kobj_attr.attr.name = #_name;		\
168		data->_name##_kobj_attr.attr.mode = 0644;		\
169	} while (0)
170
171#define init_attribute_ro(_name)					\
172	do {								\
173		sysfs_attr_init(&data->_name##_kobj_attr.attr);	\
174		data->_name##_kobj_attr.show = show_##_name;		\
175		data->_name##_kobj_attr.store = NULL;			\
176		data->_name##_kobj_attr.attr.name = #_name;		\
177		data->_name##_kobj_attr.attr.mode = 0444;		\
178	} while (0)
179
180#define init_attribute_root_ro(_name)					\
181	do {								\
182		sysfs_attr_init(&data->_name##_kobj_attr.attr);	\
183		data->_name##_kobj_attr.show = show_##_name;		\
184		data->_name##_kobj_attr.store = NULL;			\
185		data->_name##_kobj_attr.attr.name = #_name;		\
186		data->_name##_kobj_attr.attr.mode = 0400;		\
187	} while (0)
188
189static int create_attr_group(struct uncore_data *data, char *name)
190{
191	int ret, index = 0;
192	unsigned int val;
193
194	init_attribute_rw(max_freq_khz);
195	init_attribute_rw(min_freq_khz);
196	init_attribute_ro(initial_min_freq_khz);
197	init_attribute_ro(initial_max_freq_khz);
198	init_attribute_root_ro(current_freq_khz);
199
200	if (data->domain_id != UNCORE_DOMAIN_ID_INVALID) {
201		init_attribute_root_ro(domain_id);
202		data->uncore_attrs[index++] = &data->domain_id_kobj_attr.attr;
203		init_attribute_root_ro(fabric_cluster_id);
204		data->uncore_attrs[index++] = &data->fabric_cluster_id_kobj_attr.attr;
205		init_attribute_root_ro(package_id);
206		data->uncore_attrs[index++] = &data->package_id_kobj_attr.attr;
207		if (data->agent_type_mask) {
208			init_attribute_ro(agent_types);
209			data->uncore_attrs[index++] = &data->agent_types_kobj_attr.attr;
210		}
211		if (topology_max_dies_per_package() > 1 &&
212		    data->agent_type_mask & AGENT_TYPE_CORE) {
213			init_attribute_ro(die_id);
214			data->uncore_attrs[index++] = &data->die_id_kobj_attr.attr;
215		}
216	}
217
218	data->uncore_attrs[index++] = &data->max_freq_khz_kobj_attr.attr;
219	data->uncore_attrs[index++] = &data->min_freq_khz_kobj_attr.attr;
220	data->uncore_attrs[index++] = &data->initial_min_freq_khz_kobj_attr.attr;
221	data->uncore_attrs[index++] = &data->initial_max_freq_khz_kobj_attr.attr;
222
223	ret = uncore_read(data, &val, UNCORE_INDEX_CURRENT_FREQ);
224	if (!ret)
225		data->uncore_attrs[index++] = &data->current_freq_khz_kobj_attr.attr;
226
227	ret = uncore_read(data, &val, UNCORE_INDEX_EFF_LAT_CTRL_LOW_THRESHOLD);
228	if (!ret) {
229		init_attribute_rw(elc_low_threshold_percent);
230		init_attribute_rw(elc_high_threshold_percent);
231		init_attribute_rw(elc_high_threshold_enable);
232		init_attribute_rw(elc_floor_freq_khz);
233
234		data->uncore_attrs[index++] = &data->elc_low_threshold_percent_kobj_attr.attr;
235		data->uncore_attrs[index++] = &data->elc_high_threshold_percent_kobj_attr.attr;
236		data->uncore_attrs[index++] =
237			&data->elc_high_threshold_enable_kobj_attr.attr;
238		data->uncore_attrs[index++] = &data->elc_floor_freq_khz_kobj_attr.attr;
239	}
240
241	data->uncore_attrs[index] = NULL;
242
243	data->uncore_attr_group.name = name;
244	data->uncore_attr_group.attrs = data->uncore_attrs;
245	ret = sysfs_create_group(uncore_root_kobj, &data->uncore_attr_group);
246
247	return ret;
248}
249
250static void delete_attr_group(struct uncore_data *data, char *name)
251{
252	sysfs_remove_group(uncore_root_kobj, &data->uncore_attr_group);
253}
254
255int uncore_freq_add_entry(struct uncore_data *data, int cpu)
256{
257	int ret = 0;
258
259	mutex_lock(&uncore_lock);
260	if (data->valid) {
261		/* control cpu changed */
262		data->control_cpu = cpu;
263		goto uncore_unlock;
264	}
265
266	if (data->domain_id != UNCORE_DOMAIN_ID_INVALID) {
267		ret = ida_alloc(&intel_uncore_ida, GFP_KERNEL);
268		if (ret < 0)
269			goto uncore_unlock;
270
271		data->instance_id = ret;
272		sprintf(data->name, "uncore%02d", ret);
273	} else {
274		sprintf(data->name, "package_%02d_die_%02d", data->package_id, data->die_id);
275	}
276
277	uncore_read(data, &data->initial_min_freq_khz, UNCORE_INDEX_MIN_FREQ);
278	uncore_read(data, &data->initial_max_freq_khz, UNCORE_INDEX_MAX_FREQ);
279
280	ret = create_attr_group(data, data->name);
281	if (ret) {
282		if (data->domain_id != UNCORE_DOMAIN_ID_INVALID)
283			ida_free(&intel_uncore_ida, data->instance_id);
284	} else {
285		data->control_cpu = cpu;
286		data->valid = true;
287	}
288
289uncore_unlock:
290	mutex_unlock(&uncore_lock);
291
292	return ret;
293}
294EXPORT_SYMBOL_NS_GPL(uncore_freq_add_entry, "INTEL_UNCORE_FREQUENCY");
295
296void uncore_freq_remove_die_entry(struct uncore_data *data)
297{
298	mutex_lock(&uncore_lock);
299	delete_attr_group(data, data->name);
300	data->control_cpu = -1;
301	data->valid = false;
302	if (data->domain_id != UNCORE_DOMAIN_ID_INVALID)
303		ida_free(&intel_uncore_ida, data->instance_id);
304
305	mutex_unlock(&uncore_lock);
306}
307EXPORT_SYMBOL_NS_GPL(uncore_freq_remove_die_entry, "INTEL_UNCORE_FREQUENCY");
308
309int uncore_freq_common_init(int (*read)(struct uncore_data *data, unsigned int *value,
310					enum uncore_index index),
311			    int (*write)(struct uncore_data *data, unsigned int input,
312					 enum uncore_index index))
313{
314	mutex_lock(&uncore_lock);
315
316	uncore_read = read;
317	uncore_write = write;
318
319	if (!uncore_root_kobj) {
320		struct device *dev_root = bus_get_dev_root(&cpu_subsys);
321
322		if (dev_root) {
323			uncore_root_kobj = kobject_create_and_add("intel_uncore_frequency",
324								  &dev_root->kobj);
325			put_device(dev_root);
326		}
327	}
328	if (uncore_root_kobj)
329		++uncore_instance_count;
330	mutex_unlock(&uncore_lock);
331
332	return uncore_root_kobj ? 0 : -ENOMEM;
333}
334EXPORT_SYMBOL_NS_GPL(uncore_freq_common_init, "INTEL_UNCORE_FREQUENCY");
335
336void uncore_freq_common_exit(void)
337{
338	mutex_lock(&uncore_lock);
339	--uncore_instance_count;
340	if (!uncore_instance_count) {
341		kobject_put(uncore_root_kobj);
342		uncore_root_kobj = NULL;
343	}
344	mutex_unlock(&uncore_lock);
345}
346EXPORT_SYMBOL_NS_GPL(uncore_freq_common_exit, "INTEL_UNCORE_FREQUENCY");
347
348
349MODULE_LICENSE("GPL v2");
350MODULE_DESCRIPTION("Intel Uncore Frequency Common Module");
~/ Read-only file preview.