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
show_domain_id(struct kobject * kobj,struct kobj_attribute * attr,char * buf)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
show_fabric_cluster_id(struct kobject * kobj,struct kobj_attribute * attr,char * buf)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
show_package_id(struct kobject * kobj,struct kobj_attribute * attr,char * buf)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
show_min_max_freq_khz(struct uncore_data * data,char * buf,int min_max)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
store_min_max_freq_khz(struct uncore_data * data,const char * buf,ssize_t count,int min_max)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
show_perf_status_freq_khz(struct uncore_data * data,char * buf)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
create_attr_group(struct uncore_data * data,char * name)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
delete_attr_group(struct uncore_data * data,char * name)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
uncore_freq_add_entry(struct uncore_data * data,int cpu)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
uncore_freq_remove_die_entry(struct uncore_data * data)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
uncore_freq_common_init(int (* read_control_freq)(struct uncore_data * data,unsigned int * min,unsigned int * max),int (* write_control_freq)(struct uncore_data * data,unsigned int input,unsigned int set_max),int (* read_freq)(struct uncore_data * data,unsigned int * freq))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
uncore_freq_common_exit(void)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