1 // SPDX-License-Identifier: GPL-2.0
3 * Intel Uncore Frequency Control: Common code implementation
4 * Copyright (c) 2022, Intel Corporation.
9 #include <linux/module.h>
10 #include "uncore-frequency-common.h"
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;
19 static DEFINE_IDA(intel_uncore_ida);
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);
25 static ssize_t show_domain_id(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
27 struct uncore_data *data = container_of(attr, struct uncore_data, domain_id_kobj_attr);
29 return sprintf(buf, "%u\n", data->domain_id);
32 static ssize_t show_fabric_cluster_id(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
34 struct uncore_data *data = container_of(attr, struct uncore_data, fabric_cluster_id_kobj_attr);
36 return sprintf(buf, "%u\n", data->cluster_id);
39 static ssize_t show_package_id(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
41 struct uncore_data *data = container_of(attr, struct uncore_data, package_id_kobj_attr);
43 return sprintf(buf, "%u\n", data->package_id);
46 static ssize_t show_attr(struct uncore_data *data, char *buf, enum uncore_index index)
51 mutex_lock(&uncore_lock);
52 ret = uncore_read(data, &value, index);
53 mutex_unlock(&uncore_lock);
57 return sprintf(buf, "%u\n", value);
60 static ssize_t store_attr(struct uncore_data *data, const char *buf, ssize_t count,
61 enum uncore_index index)
66 if (kstrtouint(buf, 10, &input))
69 mutex_lock(&uncore_lock);
70 ret = uncore_write(data, input, index);
71 mutex_unlock(&uncore_lock);
79 #define store_uncore_attr(name, index) \
80 static ssize_t store_##name(struct kobject *kobj, \
81 struct kobj_attribute *attr, \
82 const char *buf, size_t count) \
84 struct uncore_data *data = container_of(attr, struct uncore_data, name##_kobj_attr);\
86 return store_attr(data, buf, count, index); \
89 #define show_uncore_attr(name, index) \
90 static ssize_t show_##name(struct kobject *kobj, \
91 struct kobj_attribute *attr, char *buf)\
93 struct uncore_data *data = container_of(attr, struct uncore_data, name##_kobj_attr);\
95 return show_attr(data, buf, index); \
98 store_uncore_attr(min_freq_khz, UNCORE_INDEX_MIN_FREQ);
99 store_uncore_attr(max_freq_khz, UNCORE_INDEX_MAX_FREQ);
101 show_uncore_attr(min_freq_khz, UNCORE_INDEX_MIN_FREQ);
102 show_uncore_attr(max_freq_khz, UNCORE_INDEX_MAX_FREQ);
104 show_uncore_attr(current_freq_khz, UNCORE_INDEX_CURRENT_FREQ);
106 #define show_uncore_data(member_name) \
107 static ssize_t show_##member_name(struct kobject *kobj, \
108 struct kobj_attribute *attr, char *buf)\
110 struct uncore_data *data = container_of(attr, struct uncore_data,\
111 member_name##_kobj_attr);\
113 return sysfs_emit(buf, "%u\n", \
114 data->member_name); \
117 show_uncore_data(initial_min_freq_khz);
118 show_uncore_data(initial_max_freq_khz);
120 #define init_attribute_rw(_name) \
122 sysfs_attr_init(&data->_name##_kobj_attr.attr); \
123 data->_name##_kobj_attr.show = show_##_name; \
124 data->_name##_kobj_attr.store = store_##_name; \
125 data->_name##_kobj_attr.attr.name = #_name; \
126 data->_name##_kobj_attr.attr.mode = 0644; \
129 #define init_attribute_ro(_name) \
131 sysfs_attr_init(&data->_name##_kobj_attr.attr); \
132 data->_name##_kobj_attr.show = show_##_name; \
133 data->_name##_kobj_attr.store = NULL; \
134 data->_name##_kobj_attr.attr.name = #_name; \
135 data->_name##_kobj_attr.attr.mode = 0444; \
138 #define init_attribute_root_ro(_name) \
140 sysfs_attr_init(&data->_name##_kobj_attr.attr); \
141 data->_name##_kobj_attr.show = show_##_name; \
142 data->_name##_kobj_attr.store = NULL; \
143 data->_name##_kobj_attr.attr.name = #_name; \
144 data->_name##_kobj_attr.attr.mode = 0400; \
147 static int create_attr_group(struct uncore_data *data, char *name)
149 int ret, freq, index = 0;
151 init_attribute_rw(max_freq_khz);
152 init_attribute_rw(min_freq_khz);
153 init_attribute_ro(initial_min_freq_khz);
154 init_attribute_ro(initial_max_freq_khz);
155 init_attribute_root_ro(current_freq_khz);
157 if (data->domain_id != UNCORE_DOMAIN_ID_INVALID) {
158 init_attribute_root_ro(domain_id);
159 data->uncore_attrs[index++] = &data->domain_id_kobj_attr.attr;
160 init_attribute_root_ro(fabric_cluster_id);
161 data->uncore_attrs[index++] = &data->fabric_cluster_id_kobj_attr.attr;
162 init_attribute_root_ro(package_id);
163 data->uncore_attrs[index++] = &data->package_id_kobj_attr.attr;
166 data->uncore_attrs[index++] = &data->max_freq_khz_kobj_attr.attr;
167 data->uncore_attrs[index++] = &data->min_freq_khz_kobj_attr.attr;
168 data->uncore_attrs[index++] = &data->initial_min_freq_khz_kobj_attr.attr;
169 data->uncore_attrs[index++] = &data->initial_max_freq_khz_kobj_attr.attr;
171 ret = uncore_read(data, &freq, UNCORE_INDEX_CURRENT_FREQ);
173 data->uncore_attrs[index++] = &data->current_freq_khz_kobj_attr.attr;
175 data->uncore_attrs[index] = NULL;
177 data->uncore_attr_group.name = name;
178 data->uncore_attr_group.attrs = data->uncore_attrs;
179 ret = sysfs_create_group(uncore_root_kobj, &data->uncore_attr_group);
184 static void delete_attr_group(struct uncore_data *data, char *name)
186 sysfs_remove_group(uncore_root_kobj, &data->uncore_attr_group);
189 int uncore_freq_add_entry(struct uncore_data *data, int cpu)
193 mutex_lock(&uncore_lock);
195 /* control cpu changed */
196 data->control_cpu = cpu;
200 if (data->domain_id != UNCORE_DOMAIN_ID_INVALID) {
201 ret = ida_alloc(&intel_uncore_ida, GFP_KERNEL);
205 data->instance_id = ret;
206 sprintf(data->name, "uncore%02d", ret);
208 sprintf(data->name, "package_%02d_die_%02d", data->package_id, data->die_id);
211 uncore_read(data, &data->initial_min_freq_khz, UNCORE_INDEX_MIN_FREQ);
212 uncore_read(data, &data->initial_max_freq_khz, UNCORE_INDEX_MAX_FREQ);
214 ret = create_attr_group(data, data->name);
216 if (data->domain_id != UNCORE_DOMAIN_ID_INVALID)
217 ida_free(&intel_uncore_ida, data->instance_id);
219 data->control_cpu = cpu;
224 mutex_unlock(&uncore_lock);
228 EXPORT_SYMBOL_NS_GPL(uncore_freq_add_entry, INTEL_UNCORE_FREQUENCY);
230 void uncore_freq_remove_die_entry(struct uncore_data *data)
232 mutex_lock(&uncore_lock);
233 delete_attr_group(data, data->name);
234 data->control_cpu = -1;
236 if (data->domain_id != UNCORE_DOMAIN_ID_INVALID)
237 ida_free(&intel_uncore_ida, data->instance_id);
239 mutex_unlock(&uncore_lock);
241 EXPORT_SYMBOL_NS_GPL(uncore_freq_remove_die_entry, INTEL_UNCORE_FREQUENCY);
243 int uncore_freq_common_init(int (*read)(struct uncore_data *data, unsigned int *value,
244 enum uncore_index index),
245 int (*write)(struct uncore_data *data, unsigned int input,
246 enum uncore_index index))
248 mutex_lock(&uncore_lock);
251 uncore_write = write;
253 if (!uncore_root_kobj) {
254 struct device *dev_root = bus_get_dev_root(&cpu_subsys);
257 uncore_root_kobj = kobject_create_and_add("intel_uncore_frequency",
259 put_device(dev_root);
262 if (uncore_root_kobj)
263 ++uncore_instance_count;
264 mutex_unlock(&uncore_lock);
266 return uncore_root_kobj ? 0 : -ENOMEM;
268 EXPORT_SYMBOL_NS_GPL(uncore_freq_common_init, INTEL_UNCORE_FREQUENCY);
270 void uncore_freq_common_exit(void)
272 mutex_lock(&uncore_lock);
273 --uncore_instance_count;
274 if (!uncore_instance_count) {
275 kobject_put(uncore_root_kobj);
276 uncore_root_kobj = NULL;
278 mutex_unlock(&uncore_lock);
280 EXPORT_SYMBOL_NS_GPL(uncore_freq_common_exit, INTEL_UNCORE_FREQUENCY);
283 MODULE_LICENSE("GPL v2");
284 MODULE_DESCRIPTION("Intel Uncore Frequency Common Module");