uncore-frequency-common.c (7467B)
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 19/* callbacks for actual HW read/write */ 20static int (*uncore_read)(struct uncore_data *data, unsigned int *min, unsigned int *max); 21static int (*uncore_write)(struct uncore_data *data, unsigned int input, unsigned int min_max); 22static int (*uncore_read_freq)(struct uncore_data *data, unsigned int *freq); 23 24static 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 42static 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 58static 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 101store_uncore_min_max(min_freq_khz, 0); 102store_uncore_min_max(max_freq_khz, 1); 103 104show_uncore_min_max(min_freq_khz, 0); 105show_uncore_min_max(max_freq_khz, 1); 106 107show_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 scnprintf(buf, PAGE_SIZE, "%u\n", \ 117 data->member_name); \ 118 } \ 119 120show_uncore_data(initial_min_freq_khz); 121show_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 150static 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 174static void delete_attr_group(struct uncore_data *data, char *name) 175{ 176 sysfs_remove_group(uncore_root_kobj, &data->uncore_attr_group); 177} 178 179int 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 200uncore_unlock: 201 mutex_unlock(&uncore_lock); 202 203 return ret; 204} 205EXPORT_SYMBOL_NS_GPL(uncore_freq_add_entry, INTEL_UNCORE_FREQUENCY); 206 207void 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} 215EXPORT_SYMBOL_NS_GPL(uncore_freq_remove_die_entry, INTEL_UNCORE_FREQUENCY); 216 217int 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} 236EXPORT_SYMBOL_NS_GPL(uncore_freq_common_init, INTEL_UNCORE_FREQUENCY); 237 238void 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} 248EXPORT_SYMBOL_NS_GPL(uncore_freq_common_exit, INTEL_UNCORE_FREQUENCY); 249 250 251MODULE_LICENSE("GPL v2"); 252MODULE_DESCRIPTION("Intel Uncore Frequency Common Module");