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