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