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 */
13static DEFINE_MUTEX(uncore_lock);
14/* Root of the all uncore sysfs kobjs */
15static struct kobject *uncore_root_kobj;
16/* uncore instance count */
17static int uncore_instance_count;
18
19static DEFINE_IDA(intel_uncore_ida);
20
21/* callbacks for actual HW read/write */
22static int (*uncore_read)(struct uncore_data *data, unsigned int *min, unsigned int *max);
23static int (*uncore_write)(struct uncore_data *data, unsigned int input, unsigned int min_max);
24static int (*uncore_read_freq)(struct uncore_data *data, unsigned int *freq);
25
26static 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
33static 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
40static 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
47static 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
65static 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
85static 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
128store_uncore_min_max(min_freq_khz, 0);
129store_uncore_min_max(max_freq_khz, 1);
130
131show_uncore_min_max(min_freq_khz, 0);
132show_uncore_min_max(max_freq_khz, 1);
133
134show_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
147show_uncore_data(initial_min_freq_khz);
148show_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
177static 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
214static void delete_attr_group(struct uncore_data *data, char *name)
215{
216	sysfs_remove_group(uncore_root_kobj, &data->uncore_attr_group);
217}
218
219int 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
252uncore_unlock:
253	mutex_unlock(&uncore_lock);
254
255	return ret;
256}
257EXPORT_SYMBOL_NS_GPL(uncore_freq_add_entry, INTEL_UNCORE_FREQUENCY);
258
259void 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}
270EXPORT_SYMBOL_NS_GPL(uncore_freq_remove_die_entry, INTEL_UNCORE_FREQUENCY);
271
272int 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}
297EXPORT_SYMBOL_NS_GPL(uncore_freq_common_init, INTEL_UNCORE_FREQUENCY);
298
299void 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}
309EXPORT_SYMBOL_NS_GPL(uncore_freq_common_exit, INTEL_UNCORE_FREQUENCY);
310
311
312MODULE_LICENSE("GPL v2");
313MODULE_DESCRIPTION("Intel Uncore Frequency Common Module");
314