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 *min, unsigned int *max);
23 static int (*uncore_write)(struct uncore_data *data, unsigned int input, unsigned int min_max);
24 static int (*uncore_read_freq)(struct uncore_data *data, unsigned int *freq);
25 
26 static ssize_t show_domain_id(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
27 {
28 	struct uncore_data *data = container_of(attr, struct uncore_data, domain_id_kobj_attr);
29 
30 	return sprintf(buf, "%u\n", data->domain_id);
31 }
32 
33 static ssize_t show_fabric_cluster_id(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
34 {
35 	struct uncore_data *data = container_of(attr, struct uncore_data, fabric_cluster_id_kobj_attr);
36 
37 	return sprintf(buf, "%u\n", data->cluster_id);
38 }
39 
40 static ssize_t show_package_id(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
41 {
42 	struct uncore_data *data = container_of(attr, struct uncore_data, package_id_kobj_attr);
43 
44 	return sprintf(buf, "%u\n", data->package_id);
45 }
46 
47 static ssize_t show_min_max_freq_khz(struct uncore_data *data,
48 				      char *buf, int min_max)
49 {
50 	unsigned int min, max;
51 	int ret;
52 
53 	mutex_lock(&uncore_lock);
54 	ret = uncore_read(data, &min, &max);
55 	mutex_unlock(&uncore_lock);
56 	if (ret)
57 		return ret;
58 
59 	if (min_max)
60 		return sprintf(buf, "%u\n", max);
61 
62 	return sprintf(buf, "%u\n", min);
63 }
64 
65 static ssize_t store_min_max_freq_khz(struct uncore_data *data,
66 				      const char *buf, ssize_t count,
67 				      int min_max)
68 {
69 	unsigned int input;
70 	int ret;
71 
72 	if (kstrtouint(buf, 10, &input))
73 		return -EINVAL;
74 
75 	mutex_lock(&uncore_lock);
76 	ret = uncore_write(data, input, min_max);
77 	mutex_unlock(&uncore_lock);
78 
79 	if (ret)
80 		return ret;
81 
82 	return count;
83 }
84 
85 static ssize_t show_perf_status_freq_khz(struct uncore_data *data, char *buf)
86 {
87 	unsigned int freq;
88 	int ret;
89 
90 	mutex_lock(&uncore_lock);
91 	ret = uncore_read_freq(data, &freq);
92 	mutex_unlock(&uncore_lock);
93 	if (ret)
94 		return ret;
95 
96 	return sprintf(buf, "%u\n", freq);
97 }
98 
99 #define store_uncore_min_max(name, min_max)				\
100 	static ssize_t store_##name(struct kobject *kobj,		\
101 				     struct kobj_attribute *attr,	\
102 				     const char *buf, size_t count)	\
103 	{								\
104 		struct uncore_data *data = container_of(attr, struct uncore_data, name##_kobj_attr);\
105 									\
106 		return store_min_max_freq_khz(data, buf, count,	\
107 					      min_max);		\
108 	}
109 
110 #define show_uncore_min_max(name, min_max)				\
111 	static ssize_t show_##name(struct kobject *kobj,		\
112 				    struct kobj_attribute *attr, char *buf)\
113 	{                                                               \
114 		struct uncore_data *data = container_of(attr, struct uncore_data, name##_kobj_attr);\
115 									\
116 		return show_min_max_freq_khz(data, buf, min_max);	\
117 	}
118 
119 #define show_uncore_perf_status(name)					\
120 	static ssize_t show_##name(struct kobject *kobj,		\
121 				   struct kobj_attribute *attr, char *buf)\
122 	{                                                               \
123 		struct uncore_data *data = container_of(attr, struct uncore_data, name##_kobj_attr);\
124 									\
125 		return show_perf_status_freq_khz(data, buf); \
126 	}
127 
128 store_uncore_min_max(min_freq_khz, 0);
129 store_uncore_min_max(max_freq_khz, 1);
130 
131 show_uncore_min_max(min_freq_khz, 0);
132 show_uncore_min_max(max_freq_khz, 1);
133 
134 show_uncore_perf_status(current_freq_khz);
135 
136 #define show_uncore_data(member_name)					\
137 	static ssize_t show_##member_name(struct kobject *kobj,	\
138 					   struct kobj_attribute *attr, char *buf)\
139 	{                                                               \
140 		struct uncore_data *data = container_of(attr, struct uncore_data,\
141 							  member_name##_kobj_attr);\
142 									\
143 		return sysfs_emit(buf, "%u\n",				\
144 				 data->member_name);			\
145 	}								\
146 
147 show_uncore_data(initial_min_freq_khz);
148 show_uncore_data(initial_max_freq_khz);
149 
150 #define init_attribute_rw(_name)					\
151 	do {								\
152 		sysfs_attr_init(&data->_name##_kobj_attr.attr);	\
153 		data->_name##_kobj_attr.show = show_##_name;		\
154 		data->_name##_kobj_attr.store = store_##_name;		\
155 		data->_name##_kobj_attr.attr.name = #_name;		\
156 		data->_name##_kobj_attr.attr.mode = 0644;		\
157 	} while (0)
158 
159 #define init_attribute_ro(_name)					\
160 	do {								\
161 		sysfs_attr_init(&data->_name##_kobj_attr.attr);	\
162 		data->_name##_kobj_attr.show = show_##_name;		\
163 		data->_name##_kobj_attr.store = NULL;			\
164 		data->_name##_kobj_attr.attr.name = #_name;		\
165 		data->_name##_kobj_attr.attr.mode = 0444;		\
166 	} while (0)
167 
168 #define init_attribute_root_ro(_name)					\
169 	do {								\
170 		sysfs_attr_init(&data->_name##_kobj_attr.attr);	\
171 		data->_name##_kobj_attr.show = show_##_name;		\
172 		data->_name##_kobj_attr.store = NULL;			\
173 		data->_name##_kobj_attr.attr.name = #_name;		\
174 		data->_name##_kobj_attr.attr.mode = 0400;		\
175 	} while (0)
176 
177 static int create_attr_group(struct uncore_data *data, char *name)
178 {
179 	int ret, freq, index = 0;
180 
181 	init_attribute_rw(max_freq_khz);
182 	init_attribute_rw(min_freq_khz);
183 	init_attribute_ro(initial_min_freq_khz);
184 	init_attribute_ro(initial_max_freq_khz);
185 	init_attribute_root_ro(current_freq_khz);
186 
187 	if (data->domain_id != UNCORE_DOMAIN_ID_INVALID) {
188 		init_attribute_root_ro(domain_id);
189 		data->uncore_attrs[index++] = &data->domain_id_kobj_attr.attr;
190 		init_attribute_root_ro(fabric_cluster_id);
191 		data->uncore_attrs[index++] = &data->fabric_cluster_id_kobj_attr.attr;
192 		init_attribute_root_ro(package_id);
193 		data->uncore_attrs[index++] = &data->package_id_kobj_attr.attr;
194 	}
195 
196 	data->uncore_attrs[index++] = &data->max_freq_khz_kobj_attr.attr;
197 	data->uncore_attrs[index++] = &data->min_freq_khz_kobj_attr.attr;
198 	data->uncore_attrs[index++] = &data->initial_min_freq_khz_kobj_attr.attr;
199 	data->uncore_attrs[index++] = &data->initial_max_freq_khz_kobj_attr.attr;
200 
201 	ret = uncore_read_freq(data, &freq);
202 	if (!ret)
203 		data->uncore_attrs[index++] = &data->current_freq_khz_kobj_attr.attr;
204 
205 	data->uncore_attrs[index] = NULL;
206 
207 	data->uncore_attr_group.name = name;
208 	data->uncore_attr_group.attrs = data->uncore_attrs;
209 	ret = sysfs_create_group(uncore_root_kobj, &data->uncore_attr_group);
210 
211 	return ret;
212 }
213 
214 static void delete_attr_group(struct uncore_data *data, char *name)
215 {
216 	sysfs_remove_group(uncore_root_kobj, &data->uncore_attr_group);
217 }
218 
219 int uncore_freq_add_entry(struct uncore_data *data, int cpu)
220 {
221 	int ret = 0;
222 
223 	mutex_lock(&uncore_lock);
224 	if (data->valid) {
225 		/* control cpu changed */
226 		data->control_cpu = cpu;
227 		goto uncore_unlock;
228 	}
229 
230 	if (data->domain_id != UNCORE_DOMAIN_ID_INVALID) {
231 		ret = ida_alloc(&intel_uncore_ida, GFP_KERNEL);
232 		if (ret < 0)
233 			goto uncore_unlock;
234 
235 		data->instance_id = ret;
236 		sprintf(data->name, "uncore%02d", ret);
237 	} else {
238 		sprintf(data->name, "package_%02d_die_%02d", data->package_id, data->die_id);
239 	}
240 
241 	uncore_read(data, &data->initial_min_freq_khz, &data->initial_max_freq_khz);
242 
243 	ret = create_attr_group(data, data->name);
244 	if (ret) {
245 		if (data->domain_id != UNCORE_DOMAIN_ID_INVALID)
246 			ida_free(&intel_uncore_ida, data->instance_id);
247 	} else {
248 		data->control_cpu = cpu;
249 		data->valid = true;
250 	}
251 
252 uncore_unlock:
253 	mutex_unlock(&uncore_lock);
254 
255 	return ret;
256 }
257 EXPORT_SYMBOL_NS_GPL(uncore_freq_add_entry, INTEL_UNCORE_FREQUENCY);
258 
259 void uncore_freq_remove_die_entry(struct uncore_data *data)
260 {
261 	mutex_lock(&uncore_lock);
262 	delete_attr_group(data, data->name);
263 	data->control_cpu = -1;
264 	data->valid = false;
265 	if (data->domain_id != UNCORE_DOMAIN_ID_INVALID)
266 		ida_free(&intel_uncore_ida, data->instance_id);
267 
268 	mutex_unlock(&uncore_lock);
269 }
270 EXPORT_SYMBOL_NS_GPL(uncore_freq_remove_die_entry, INTEL_UNCORE_FREQUENCY);
271 
272 int uncore_freq_common_init(int (*read_control_freq)(struct uncore_data *data, unsigned int *min, unsigned int *max),
273 			     int (*write_control_freq)(struct uncore_data *data, unsigned int input, unsigned int set_max),
274 			     int (*read_freq)(struct uncore_data *data, unsigned int *freq))
275 {
276 	mutex_lock(&uncore_lock);
277 
278 	uncore_read = read_control_freq;
279 	uncore_write = write_control_freq;
280 	uncore_read_freq = read_freq;
281 
282 	if (!uncore_root_kobj) {
283 		struct device *dev_root = bus_get_dev_root(&cpu_subsys);
284 
285 		if (dev_root) {
286 			uncore_root_kobj = kobject_create_and_add("intel_uncore_frequency",
287 								  &dev_root->kobj);
288 			put_device(dev_root);
289 		}
290 	}
291 	if (uncore_root_kobj)
292 		++uncore_instance_count;
293 	mutex_unlock(&uncore_lock);
294 
295 	return uncore_root_kobj ? 0 : -ENOMEM;
296 }
297 EXPORT_SYMBOL_NS_GPL(uncore_freq_common_init, INTEL_UNCORE_FREQUENCY);
298 
299 void uncore_freq_common_exit(void)
300 {
301 	mutex_lock(&uncore_lock);
302 	--uncore_instance_count;
303 	if (!uncore_instance_count) {
304 		kobject_put(uncore_root_kobj);
305 		uncore_root_kobj = NULL;
306 	}
307 	mutex_unlock(&uncore_lock);
308 }
309 EXPORT_SYMBOL_NS_GPL(uncore_freq_common_exit, INTEL_UNCORE_FREQUENCY);
310 
311 
312 MODULE_LICENSE("GPL v2");
313 MODULE_DESCRIPTION("Intel Uncore Frequency Common Module");
314