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 /* callbacks for actual HW read/write */
20 static int (*uncore_read)(struct uncore_data *data, unsigned int *min, unsigned int *max);
21 static int (*uncore_write)(struct uncore_data *data, unsigned int input, unsigned int min_max);
22 static int (*uncore_read_freq)(struct uncore_data *data, unsigned int *freq);
23
show_min_max_freq_khz(struct uncore_data * data,char * buf,int min_max)24 static ssize_t show_min_max_freq_khz(struct uncore_data *data,
25 char *buf, int min_max)
26 {
27 unsigned int min, max;
28 int ret;
29
30 mutex_lock(&uncore_lock);
31 ret = uncore_read(data, &min, &max);
32 mutex_unlock(&uncore_lock);
33 if (ret)
34 return ret;
35
36 if (min_max)
37 return sprintf(buf, "%u\n", max);
38
39 return sprintf(buf, "%u\n", min);
40 }
41
store_min_max_freq_khz(struct uncore_data * data,const char * buf,ssize_t count,int min_max)42 static ssize_t store_min_max_freq_khz(struct uncore_data *data,
43 const char *buf, ssize_t count,
44 int min_max)
45 {
46 unsigned int input;
47
48 if (kstrtouint(buf, 10, &input))
49 return -EINVAL;
50
51 mutex_lock(&uncore_lock);
52 uncore_write(data, input, min_max);
53 mutex_unlock(&uncore_lock);
54
55 return count;
56 }
57
show_perf_status_freq_khz(struct uncore_data * data,char * buf)58 static ssize_t show_perf_status_freq_khz(struct uncore_data *data, char *buf)
59 {
60 unsigned int freq;
61 int ret;
62
63 mutex_lock(&uncore_lock);
64 ret = uncore_read_freq(data, &freq);
65 mutex_unlock(&uncore_lock);
66 if (ret)
67 return ret;
68
69 return sprintf(buf, "%u\n", freq);
70 }
71
72 #define store_uncore_min_max(name, min_max) \
73 static ssize_t store_##name(struct device *dev, \
74 struct device_attribute *attr, \
75 const char *buf, size_t count) \
76 { \
77 struct uncore_data *data = container_of(attr, struct uncore_data, name##_dev_attr);\
78 \
79 return store_min_max_freq_khz(data, buf, count, \
80 min_max); \
81 }
82
83 #define show_uncore_min_max(name, min_max) \
84 static ssize_t show_##name(struct device *dev, \
85 struct device_attribute *attr, char *buf)\
86 { \
87 struct uncore_data *data = container_of(attr, struct uncore_data, name##_dev_attr);\
88 \
89 return show_min_max_freq_khz(data, buf, min_max); \
90 }
91
92 #define show_uncore_perf_status(name) \
93 static ssize_t show_##name(struct device *dev, \
94 struct device_attribute *attr, char *buf)\
95 { \
96 struct uncore_data *data = container_of(attr, struct uncore_data, name##_dev_attr);\
97 \
98 return show_perf_status_freq_khz(data, buf); \
99 }
100
101 store_uncore_min_max(min_freq_khz, 0);
102 store_uncore_min_max(max_freq_khz, 1);
103
104 show_uncore_min_max(min_freq_khz, 0);
105 show_uncore_min_max(max_freq_khz, 1);
106
107 show_uncore_perf_status(current_freq_khz);
108
109 #define show_uncore_data(member_name) \
110 static ssize_t show_##member_name(struct device *dev, \
111 struct device_attribute *attr, char *buf)\
112 { \
113 struct uncore_data *data = container_of(attr, struct uncore_data,\
114 member_name##_dev_attr);\
115 \
116 return sysfs_emit(buf, "%u\n", \
117 data->member_name); \
118 } \
119
120 show_uncore_data(initial_min_freq_khz);
121 show_uncore_data(initial_max_freq_khz);
122
123 #define init_attribute_rw(_name) \
124 do { \
125 sysfs_attr_init(&data->_name##_dev_attr.attr); \
126 data->_name##_dev_attr.show = show_##_name; \
127 data->_name##_dev_attr.store = store_##_name; \
128 data->_name##_dev_attr.attr.name = #_name; \
129 data->_name##_dev_attr.attr.mode = 0644; \
130 } while (0)
131
132 #define init_attribute_ro(_name) \
133 do { \
134 sysfs_attr_init(&data->_name##_dev_attr.attr); \
135 data->_name##_dev_attr.show = show_##_name; \
136 data->_name##_dev_attr.store = NULL; \
137 data->_name##_dev_attr.attr.name = #_name; \
138 data->_name##_dev_attr.attr.mode = 0444; \
139 } while (0)
140
141 #define init_attribute_root_ro(_name) \
142 do { \
143 sysfs_attr_init(&data->_name##_dev_attr.attr); \
144 data->_name##_dev_attr.show = show_##_name; \
145 data->_name##_dev_attr.store = NULL; \
146 data->_name##_dev_attr.attr.name = #_name; \
147 data->_name##_dev_attr.attr.mode = 0400; \
148 } while (0)
149
create_attr_group(struct uncore_data * data,char * name)150 static int create_attr_group(struct uncore_data *data, char *name)
151 {
152 int ret, index = 0;
153
154 init_attribute_rw(max_freq_khz);
155 init_attribute_rw(min_freq_khz);
156 init_attribute_ro(initial_min_freq_khz);
157 init_attribute_ro(initial_max_freq_khz);
158 init_attribute_root_ro(current_freq_khz);
159
160 data->uncore_attrs[index++] = &data->max_freq_khz_dev_attr.attr;
161 data->uncore_attrs[index++] = &data->min_freq_khz_dev_attr.attr;
162 data->uncore_attrs[index++] = &data->initial_min_freq_khz_dev_attr.attr;
163 data->uncore_attrs[index++] = &data->initial_max_freq_khz_dev_attr.attr;
164 data->uncore_attrs[index++] = &data->current_freq_khz_dev_attr.attr;
165 data->uncore_attrs[index] = NULL;
166
167 data->uncore_attr_group.name = name;
168 data->uncore_attr_group.attrs = data->uncore_attrs;
169 ret = sysfs_create_group(uncore_root_kobj, &data->uncore_attr_group);
170
171 return ret;
172 }
173
delete_attr_group(struct uncore_data * data,char * name)174 static void delete_attr_group(struct uncore_data *data, char *name)
175 {
176 sysfs_remove_group(uncore_root_kobj, &data->uncore_attr_group);
177 }
178
uncore_freq_add_entry(struct uncore_data * data,int cpu)179 int uncore_freq_add_entry(struct uncore_data *data, int cpu)
180 {
181 int ret = 0;
182
183 mutex_lock(&uncore_lock);
184 if (data->valid) {
185 /* control cpu changed */
186 data->control_cpu = cpu;
187 goto uncore_unlock;
188 }
189
190 sprintf(data->name, "package_%02d_die_%02d", data->package_id, data->die_id);
191
192 uncore_read(data, &data->initial_min_freq_khz, &data->initial_max_freq_khz);
193
194 ret = create_attr_group(data, data->name);
195 if (!ret) {
196 data->control_cpu = cpu;
197 data->valid = true;
198 }
199
200 uncore_unlock:
201 mutex_unlock(&uncore_lock);
202
203 return ret;
204 }
205 EXPORT_SYMBOL_NS_GPL(uncore_freq_add_entry, INTEL_UNCORE_FREQUENCY);
206
uncore_freq_remove_die_entry(struct uncore_data * data)207 void uncore_freq_remove_die_entry(struct uncore_data *data)
208 {
209 mutex_lock(&uncore_lock);
210 delete_attr_group(data, data->name);
211 data->control_cpu = -1;
212 data->valid = false;
213 mutex_unlock(&uncore_lock);
214 }
215 EXPORT_SYMBOL_NS_GPL(uncore_freq_remove_die_entry, INTEL_UNCORE_FREQUENCY);
216
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))217 int uncore_freq_common_init(int (*read_control_freq)(struct uncore_data *data, unsigned int *min, unsigned int *max),
218 int (*write_control_freq)(struct uncore_data *data, unsigned int input, unsigned int set_max),
219 int (*read_freq)(struct uncore_data *data, unsigned int *freq))
220 {
221 mutex_lock(&uncore_lock);
222
223 uncore_read = read_control_freq;
224 uncore_write = write_control_freq;
225 uncore_read_freq = read_freq;
226
227 if (!uncore_root_kobj)
228 uncore_root_kobj = kobject_create_and_add("intel_uncore_frequency",
229 &cpu_subsys.dev_root->kobj);
230 if (uncore_root_kobj)
231 ++uncore_instance_count;
232 mutex_unlock(&uncore_lock);
233
234 return uncore_root_kobj ? 0 : -ENOMEM;
235 }
236 EXPORT_SYMBOL_NS_GPL(uncore_freq_common_init, INTEL_UNCORE_FREQUENCY);
237
uncore_freq_common_exit(void)238 void uncore_freq_common_exit(void)
239 {
240 mutex_lock(&uncore_lock);
241 --uncore_instance_count;
242 if (!uncore_instance_count) {
243 kobject_put(uncore_root_kobj);
244 uncore_root_kobj = NULL;
245 }
246 mutex_unlock(&uncore_lock);
247 }
248 EXPORT_SYMBOL_NS_GPL(uncore_freq_common_exit, INTEL_UNCORE_FREQUENCY);
249
250
251 MODULE_LICENSE("GPL v2");
252 MODULE_DESCRIPTION("Intel Uncore Frequency Common Module");
253