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 *value, enum uncore_index index); 23 static int (*uncore_write)(struct uncore_data *data, unsigned int input, enum uncore_index index); 24 show_domain_id(struct kobject * kobj,struct kobj_attribute * attr,char * buf)25 static ssize_t show_domain_id(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 26 { 27 struct uncore_data *data = container_of(attr, struct uncore_data, domain_id_kobj_attr); 28 29 return sprintf(buf, "%u\n", data->domain_id); 30 } 31 show_fabric_cluster_id(struct kobject * kobj,struct kobj_attribute * attr,char * buf)32 static ssize_t show_fabric_cluster_id(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 33 { 34 struct uncore_data *data = container_of(attr, struct uncore_data, fabric_cluster_id_kobj_attr); 35 36 return sprintf(buf, "%u\n", data->cluster_id); 37 } 38 show_package_id(struct kobject * kobj,struct kobj_attribute * attr,char * buf)39 static ssize_t show_package_id(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 40 { 41 struct uncore_data *data = container_of(attr, struct uncore_data, package_id_kobj_attr); 42 43 return sprintf(buf, "%u\n", data->package_id); 44 } 45 46 #define MAX_UNCORE_AGENT_TYPES 4 47 48 /* The order follows AGENT_TYPE_* defines */ 49 static const char *agent_name[MAX_UNCORE_AGENT_TYPES] = {"core", "cache", "memory", "io"}; 50 show_agent_types(struct kobject * kobj,struct kobj_attribute * attr,char * buf)51 static ssize_t show_agent_types(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 52 { 53 struct uncore_data *data = container_of(attr, struct uncore_data, agent_types_kobj_attr); 54 unsigned long agent_mask = data->agent_type_mask; 55 int agent, length = 0; 56 57 for_each_set_bit(agent, &agent_mask, MAX_UNCORE_AGENT_TYPES) { 58 if (length) 59 length += sysfs_emit_at(buf, length, " "); 60 61 length += sysfs_emit_at(buf, length, "%s", agent_name[agent]); 62 } 63 64 length += sysfs_emit_at(buf, length, "\n"); 65 66 return length; 67 } 68 show_attr(struct uncore_data * data,char * buf,enum uncore_index index)69 static ssize_t show_attr(struct uncore_data *data, char *buf, enum uncore_index index) 70 { 71 unsigned int value; 72 int ret; 73 74 mutex_lock(&uncore_lock); 75 ret = uncore_read(data, &value, index); 76 mutex_unlock(&uncore_lock); 77 if (ret) 78 return ret; 79 80 return sprintf(buf, "%u\n", value); 81 } 82 store_attr(struct uncore_data * data,const char * buf,ssize_t count,enum uncore_index index)83 static ssize_t store_attr(struct uncore_data *data, const char *buf, ssize_t count, 84 enum uncore_index index) 85 { 86 unsigned int input = 0; 87 int ret; 88 89 if (index == UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD_ENABLE) { 90 if (kstrtobool(buf, (bool *)&input)) 91 return -EINVAL; 92 } else { 93 if (kstrtouint(buf, 10, &input)) 94 return -EINVAL; 95 } 96 97 mutex_lock(&uncore_lock); 98 ret = uncore_write(data, input, index); 99 mutex_unlock(&uncore_lock); 100 101 if (ret) 102 return ret; 103 104 return count; 105 } 106 107 #define store_uncore_attr(name, index) \ 108 static ssize_t store_##name(struct kobject *kobj, \ 109 struct kobj_attribute *attr, \ 110 const char *buf, size_t count) \ 111 { \ 112 struct uncore_data *data = container_of(attr, struct uncore_data, name##_kobj_attr);\ 113 \ 114 return store_attr(data, buf, count, index); \ 115 } 116 117 #define show_uncore_attr(name, index) \ 118 static ssize_t show_##name(struct kobject *kobj, \ 119 struct kobj_attribute *attr, char *buf)\ 120 { \ 121 struct uncore_data *data = container_of(attr, struct uncore_data, name##_kobj_attr);\ 122 \ 123 return show_attr(data, buf, index); \ 124 } 125 126 store_uncore_attr(min_freq_khz, UNCORE_INDEX_MIN_FREQ); 127 store_uncore_attr(max_freq_khz, UNCORE_INDEX_MAX_FREQ); 128 129 show_uncore_attr(min_freq_khz, UNCORE_INDEX_MIN_FREQ); 130 show_uncore_attr(max_freq_khz, UNCORE_INDEX_MAX_FREQ); 131 132 show_uncore_attr(current_freq_khz, UNCORE_INDEX_CURRENT_FREQ); 133 134 store_uncore_attr(elc_low_threshold_percent, UNCORE_INDEX_EFF_LAT_CTRL_LOW_THRESHOLD); 135 store_uncore_attr(elc_high_threshold_percent, UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD); 136 store_uncore_attr(elc_high_threshold_enable, 137 UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD_ENABLE); 138 store_uncore_attr(elc_floor_freq_khz, UNCORE_INDEX_EFF_LAT_CTRL_FREQ); 139 140 show_uncore_attr(elc_low_threshold_percent, UNCORE_INDEX_EFF_LAT_CTRL_LOW_THRESHOLD); 141 show_uncore_attr(elc_high_threshold_percent, UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD); 142 show_uncore_attr(elc_high_threshold_enable, 143 UNCORE_INDEX_EFF_LAT_CTRL_HIGH_THRESHOLD_ENABLE); 144 show_uncore_attr(elc_floor_freq_khz, UNCORE_INDEX_EFF_LAT_CTRL_FREQ); 145 146 show_uncore_attr(die_id, UNCORE_INDEX_DIE_ID); 147 148 #define show_uncore_data(member_name) \ 149 static ssize_t show_##member_name(struct kobject *kobj, \ 150 struct kobj_attribute *attr, char *buf)\ 151 { \ 152 struct uncore_data *data = container_of(attr, struct uncore_data,\ 153 member_name##_kobj_attr);\ 154 \ 155 return sysfs_emit(buf, "%u\n", \ 156 data->member_name); \ 157 } \ 158 159 show_uncore_data(initial_min_freq_khz); 160 show_uncore_data(initial_max_freq_khz); 161 162 #define init_attribute_rw(_name) \ 163 do { \ 164 sysfs_attr_init(&data->_name##_kobj_attr.attr); \ 165 data->_name##_kobj_attr.show = show_##_name; \ 166 data->_name##_kobj_attr.store = store_##_name; \ 167 data->_name##_kobj_attr.attr.name = #_name; \ 168 data->_name##_kobj_attr.attr.mode = 0644; \ 169 } while (0) 170 171 #define init_attribute_ro(_name) \ 172 do { \ 173 sysfs_attr_init(&data->_name##_kobj_attr.attr); \ 174 data->_name##_kobj_attr.show = show_##_name; \ 175 data->_name##_kobj_attr.store = NULL; \ 176 data->_name##_kobj_attr.attr.name = #_name; \ 177 data->_name##_kobj_attr.attr.mode = 0444; \ 178 } while (0) 179 180 #define init_attribute_root_ro(_name) \ 181 do { \ 182 sysfs_attr_init(&data->_name##_kobj_attr.attr); \ 183 data->_name##_kobj_attr.show = show_##_name; \ 184 data->_name##_kobj_attr.store = NULL; \ 185 data->_name##_kobj_attr.attr.name = #_name; \ 186 data->_name##_kobj_attr.attr.mode = 0400; \ 187 } while (0) 188 create_attr_group(struct uncore_data * data,char * name)189 static int create_attr_group(struct uncore_data *data, char *name) 190 { 191 int ret, index = 0; 192 unsigned int val; 193 194 init_attribute_rw(max_freq_khz); 195 init_attribute_rw(min_freq_khz); 196 init_attribute_ro(initial_min_freq_khz); 197 init_attribute_ro(initial_max_freq_khz); 198 init_attribute_root_ro(current_freq_khz); 199 200 if (data->domain_id != UNCORE_DOMAIN_ID_INVALID) { 201 init_attribute_root_ro(domain_id); 202 data->uncore_attrs[index++] = &data->domain_id_kobj_attr.attr; 203 init_attribute_root_ro(fabric_cluster_id); 204 data->uncore_attrs[index++] = &data->fabric_cluster_id_kobj_attr.attr; 205 init_attribute_root_ro(package_id); 206 data->uncore_attrs[index++] = &data->package_id_kobj_attr.attr; 207 if (data->agent_type_mask) { 208 init_attribute_ro(agent_types); 209 data->uncore_attrs[index++] = &data->agent_types_kobj_attr.attr; 210 } 211 if (topology_max_dies_per_package() > 1 && 212 data->agent_type_mask & AGENT_TYPE_CORE) { 213 init_attribute_ro(die_id); 214 data->uncore_attrs[index++] = &data->die_id_kobj_attr.attr; 215 } 216 } 217 218 data->uncore_attrs[index++] = &data->max_freq_khz_kobj_attr.attr; 219 data->uncore_attrs[index++] = &data->min_freq_khz_kobj_attr.attr; 220 data->uncore_attrs[index++] = &data->initial_min_freq_khz_kobj_attr.attr; 221 data->uncore_attrs[index++] = &data->initial_max_freq_khz_kobj_attr.attr; 222 223 ret = uncore_read(data, &val, UNCORE_INDEX_CURRENT_FREQ); 224 if (!ret) 225 data->uncore_attrs[index++] = &data->current_freq_khz_kobj_attr.attr; 226 227 ret = uncore_read(data, &val, UNCORE_INDEX_EFF_LAT_CTRL_LOW_THRESHOLD); 228 if (!ret) { 229 init_attribute_rw(elc_low_threshold_percent); 230 init_attribute_rw(elc_high_threshold_percent); 231 init_attribute_rw(elc_high_threshold_enable); 232 init_attribute_rw(elc_floor_freq_khz); 233 234 data->uncore_attrs[index++] = &data->elc_low_threshold_percent_kobj_attr.attr; 235 data->uncore_attrs[index++] = &data->elc_high_threshold_percent_kobj_attr.attr; 236 data->uncore_attrs[index++] = 237 &data->elc_high_threshold_enable_kobj_attr.attr; 238 data->uncore_attrs[index++] = &data->elc_floor_freq_khz_kobj_attr.attr; 239 } 240 241 data->uncore_attrs[index] = NULL; 242 243 data->uncore_attr_group.name = name; 244 data->uncore_attr_group.attrs = data->uncore_attrs; 245 ret = sysfs_create_group(uncore_root_kobj, &data->uncore_attr_group); 246 247 return ret; 248 } 249 delete_attr_group(struct uncore_data * data,char * name)250 static void delete_attr_group(struct uncore_data *data, char *name) 251 { 252 sysfs_remove_group(uncore_root_kobj, &data->uncore_attr_group); 253 } 254 uncore_freq_add_entry(struct uncore_data * data,int cpu)255 int uncore_freq_add_entry(struct uncore_data *data, int cpu) 256 { 257 int ret = 0; 258 259 mutex_lock(&uncore_lock); 260 if (data->valid) { 261 /* control cpu changed */ 262 data->control_cpu = cpu; 263 goto uncore_unlock; 264 } 265 266 if (data->domain_id != UNCORE_DOMAIN_ID_INVALID) { 267 ret = ida_alloc(&intel_uncore_ida, GFP_KERNEL); 268 if (ret < 0) 269 goto uncore_unlock; 270 271 data->instance_id = ret; 272 sprintf(data->name, "uncore%02d", ret); 273 } else { 274 sprintf(data->name, "package_%02d_die_%02d", data->package_id, data->die_id); 275 } 276 277 uncore_read(data, &data->initial_min_freq_khz, UNCORE_INDEX_MIN_FREQ); 278 uncore_read(data, &data->initial_max_freq_khz, UNCORE_INDEX_MAX_FREQ); 279 280 ret = create_attr_group(data, data->name); 281 if (ret) { 282 if (data->domain_id != UNCORE_DOMAIN_ID_INVALID) 283 ida_free(&intel_uncore_ida, data->instance_id); 284 } else { 285 data->control_cpu = cpu; 286 data->valid = true; 287 } 288 289 uncore_unlock: 290 mutex_unlock(&uncore_lock); 291 292 return ret; 293 } 294 EXPORT_SYMBOL_NS_GPL(uncore_freq_add_entry, "INTEL_UNCORE_FREQUENCY"); 295 uncore_freq_remove_die_entry(struct uncore_data * data)296 void uncore_freq_remove_die_entry(struct uncore_data *data) 297 { 298 mutex_lock(&uncore_lock); 299 delete_attr_group(data, data->name); 300 data->control_cpu = -1; 301 data->valid = false; 302 if (data->domain_id != UNCORE_DOMAIN_ID_INVALID) 303 ida_free(&intel_uncore_ida, data->instance_id); 304 305 mutex_unlock(&uncore_lock); 306 } 307 EXPORT_SYMBOL_NS_GPL(uncore_freq_remove_die_entry, "INTEL_UNCORE_FREQUENCY"); 308 uncore_freq_common_init(int (* read)(struct uncore_data * data,unsigned int * value,enum uncore_index index),int (* write)(struct uncore_data * data,unsigned int input,enum uncore_index index))309 int uncore_freq_common_init(int (*read)(struct uncore_data *data, unsigned int *value, 310 enum uncore_index index), 311 int (*write)(struct uncore_data *data, unsigned int input, 312 enum uncore_index index)) 313 { 314 mutex_lock(&uncore_lock); 315 316 uncore_read = read; 317 uncore_write = write; 318 319 if (!uncore_root_kobj) { 320 struct device *dev_root = bus_get_dev_root(&cpu_subsys); 321 322 if (dev_root) { 323 uncore_root_kobj = kobject_create_and_add("intel_uncore_frequency", 324 &dev_root->kobj); 325 put_device(dev_root); 326 } 327 } 328 if (uncore_root_kobj) 329 ++uncore_instance_count; 330 mutex_unlock(&uncore_lock); 331 332 return uncore_root_kobj ? 0 : -ENOMEM; 333 } 334 EXPORT_SYMBOL_NS_GPL(uncore_freq_common_init, "INTEL_UNCORE_FREQUENCY"); 335 uncore_freq_common_exit(void)336 void uncore_freq_common_exit(void) 337 { 338 mutex_lock(&uncore_lock); 339 --uncore_instance_count; 340 if (!uncore_instance_count) { 341 kobject_put(uncore_root_kobj); 342 uncore_root_kobj = NULL; 343 } 344 mutex_unlock(&uncore_lock); 345 } 346 EXPORT_SYMBOL_NS_GPL(uncore_freq_common_exit, "INTEL_UNCORE_FREQUENCY"); 347 348 349 MODULE_LICENSE("GPL v2"); 350 MODULE_DESCRIPTION("Intel Uncore Frequency Common Module"); 351