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