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