xref: /linux/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c (revision ab93e0dd72c37d378dd936f031ffb83ff2bd87ce) !
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 */
13  static DEFINE_MUTEX(uncore_lock);
14  /* Root of the all uncore sysfs kobjs */
15  static struct kobject *uncore_root_kobj;
16  /* uncore instance count */
17  static int uncore_instance_count;
18  
19  static DEFINE_IDA(intel_uncore_ida);
20  
21  /* callbacks for actual HW read/write */
22  static int (*uncore_read)(struct uncore_data *data, unsigned int *value, enum uncore_index index);
23  static int (*uncore_write)(struct uncore_data *data, unsigned int input, enum uncore_index index);
24  
show_domain_id(struct kobject * kobj,struct kobj_attribute * attr,char * buf)25  static 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  
show_fabric_cluster_id(struct kobject * kobj,struct kobj_attribute * attr,char * buf)32  static 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  
show_package_id(struct kobject * kobj,struct kobj_attribute * attr,char * buf)39  static 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 */
49  static const char *agent_name[MAX_UNCORE_AGENT_TYPES] = {"core", "cache", "memory", "io"};
50  
show_agent_types(struct kobject * kobj,struct kobj_attribute * attr,char * buf)51  static 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  
show_attr(struct uncore_data * data,char * buf,enum uncore_index index)69  static 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  
store_attr(struct uncore_data * data,const char * buf,ssize_t count,enum uncore_index index)83  static 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  
126  store_uncore_attr(min_freq_khz, UNCORE_INDEX_MIN_FREQ);
127  store_uncore_attr(max_freq_khz, UNCORE_INDEX_MAX_FREQ);
128  
129  show_uncore_attr(min_freq_khz, UNCORE_INDEX_MIN_FREQ);
130  show_uncore_attr(max_freq_khz, UNCORE_INDEX_MAX_FREQ);
131  
132  show_uncore_attr(current_freq_khz, UNCORE_INDEX_CURRENT_FREQ);
133  
134  store_uncore_attr(elc_low_threshold_percent, UNCORE_INDEX_EFF_LAT_CTRL_LOW_THRESHOLD);
135  store_uncore_attr(elc_high_threshold_percent, UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD);
136  store_uncore_attr(elc_high_threshold_enable,
137  		  UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD_ENABLE);
138  store_uncore_attr(elc_floor_freq_khz, UNCORE_INDEX_EFF_LAT_CTRL_FREQ);
139  
140  show_uncore_attr(elc_low_threshold_percent, UNCORE_INDEX_EFF_LAT_CTRL_LOW_THRESHOLD);
141  show_uncore_attr(elc_high_threshold_percent, UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD);
142  show_uncore_attr(elc_high_threshold_enable,
143  		 UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD_ENABLE);
144  show_uncore_attr(elc_floor_freq_khz, UNCORE_INDEX_EFF_LAT_CTRL_FREQ);
145  
146  show_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  
159  show_uncore_data(initial_min_freq_khz);
160  show_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  
create_attr_group(struct uncore_data * data,char * name)189  static 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  
delete_attr_group(struct uncore_data * data,char * name)250  static void delete_attr_group(struct uncore_data *data, char *name)
251  {
252  	sysfs_remove_group(uncore_root_kobj, &data->uncore_attr_group);
253  }
254  
uncore_freq_add_entry(struct uncore_data * data,int cpu)255  int 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  
289  uncore_unlock:
290  	mutex_unlock(&uncore_lock);
291  
292  	return ret;
293  }
294  EXPORT_SYMBOL_NS_GPL(uncore_freq_add_entry, "INTEL_UNCORE_FREQUENCY");
295  
uncore_freq_remove_die_entry(struct uncore_data * data)296  void 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  }
307  EXPORT_SYMBOL_NS_GPL(uncore_freq_remove_die_entry, "INTEL_UNCORE_FREQUENCY");
308  
uncore_freq_common_init(int (* read)(struct uncore_data * data,unsigned int * value,enum uncore_index index),int (* write)(struct uncore_data * data,unsigned int input,enum uncore_index index))309  int 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  }
334  EXPORT_SYMBOL_NS_GPL(uncore_freq_common_init, "INTEL_UNCORE_FREQUENCY");
335  
uncore_freq_common_exit(void)336  void 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  }
346  EXPORT_SYMBOL_NS_GPL(uncore_freq_common_exit, "INTEL_UNCORE_FREQUENCY");
347  
348  
349  MODULE_LICENSE("GPL v2");
350  MODULE_DESCRIPTION("Intel Uncore Frequency Common Module");
351