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