1 // SPDX-License-Identifier: GPL-2.0-only
4 * Copyright (C) 2020 Advanced Micro Devices, Inc.
6 #include <asm/cpu_device_id.h>
8 #include <linux/bits.h>
10 #include <linux/cpumask.h>
11 #include <linux/delay.h>
12 #include <linux/device.h>
13 #include <linux/hwmon.h>
14 #include <linux/kernel.h>
15 #include <linux/kthread.h>
16 #include <linux/list.h>
17 #include <linux/module.h>
18 #include <linux/mutex.h>
19 #include <linux/processor.h>
20 #include <linux/platform_device.h>
21 #include <linux/sched.h>
22 #include <linux/slab.h>
23 #include <linux/topology.h>
24 #include <linux/types.h>
26 #define DRVNAME "amd_energy"
28 #define ENERGY_PWR_UNIT_MSR 0xC0010299
29 #define ENERGY_CORE_MSR 0xC001029A
30 #define ENERGY_PKG_MSR 0xC001029B
32 #define AMD_ENERGY_UNIT_MASK 0x01F00
33 #define AMD_ENERGY_MASK 0xFFFFFFFF
35 struct sensor_accumulator {
41 struct amd_energy_data {
42 struct hwmon_channel_info energy_info;
43 const struct hwmon_channel_info *info[2];
44 struct hwmon_chip_info chip;
45 struct task_struct *wrap_accumulate;
46 /* Lock around the accumulator */
48 /* An accumulator for each core and socket */
49 struct sensor_accumulator *accums;
50 /* Energy Status Units */
57 static int amd_energy_read_labels(struct device *dev,
58 enum hwmon_sensor_types type,
59 u32 attr, int channel,
62 struct amd_energy_data *data = dev_get_drvdata(dev);
64 *str = data->accums[channel].label;
68 static void get_energy_units(struct amd_energy_data *data)
72 rdmsrl_safe(ENERGY_PWR_UNIT_MSR, &rapl_units);
73 data->energy_units = (rapl_units & AMD_ENERGY_UNIT_MASK) >> 8;
76 static void accumulate_socket_delta(struct amd_energy_data *data,
79 struct sensor_accumulator *s_accum;
82 mutex_lock(&data->lock);
83 rdmsrl_safe_on_cpu(cpu, ENERGY_PKG_MSR, &input);
84 input &= AMD_ENERGY_MASK;
86 s_accum = &data->accums[data->nr_cpus + sock];
87 if (input >= s_accum->prev_value)
88 s_accum->energy_ctr +=
89 input - s_accum->prev_value;
91 s_accum->energy_ctr += UINT_MAX -
92 s_accum->prev_value + input;
94 s_accum->prev_value = input;
95 mutex_unlock(&data->lock);
98 static void accumulate_core_delta(struct amd_energy_data *data)
100 struct sensor_accumulator *c_accum;
104 mutex_lock(&data->lock);
105 if (data->core_id >= data->nr_cpus)
110 if (!cpu_online(cpu))
113 rdmsrl_safe_on_cpu(cpu, ENERGY_CORE_MSR, &input);
114 input &= AMD_ENERGY_MASK;
116 c_accum = &data->accums[cpu];
118 if (input >= c_accum->prev_value)
119 c_accum->energy_ctr +=
120 input - c_accum->prev_value;
122 c_accum->energy_ctr += UINT_MAX -
123 c_accum->prev_value + input;
125 c_accum->prev_value = input;
129 mutex_unlock(&data->lock);
132 static void read_accumulate(struct amd_energy_data *data)
136 for (sock = 0; sock < data->nr_socks; sock++) {
139 cpu = cpumask_first_and(cpu_online_mask,
140 cpumask_of_node(sock));
142 accumulate_socket_delta(data, sock, cpu);
145 accumulate_core_delta(data);
148 static void amd_add_delta(struct amd_energy_data *data, int ch,
149 int cpu, long *val, bool is_core)
151 struct sensor_accumulator *s_accum, *c_accum;
154 mutex_lock(&data->lock);
156 rdmsrl_safe_on_cpu(cpu, ENERGY_PKG_MSR, &input);
157 input &= AMD_ENERGY_MASK;
159 s_accum = &data->accums[ch];
160 if (input >= s_accum->prev_value)
161 input += s_accum->energy_ctr -
164 input += UINT_MAX - s_accum->prev_value +
167 rdmsrl_safe_on_cpu(cpu, ENERGY_CORE_MSR, &input);
168 input &= AMD_ENERGY_MASK;
170 c_accum = &data->accums[ch];
171 if (input >= c_accum->prev_value)
172 input += c_accum->energy_ctr -
175 input += UINT_MAX - c_accum->prev_value +
179 /* Energy consumed = (1/(2^ESU) * RAW * 1000000UL) μJoules */
180 *val = div64_ul(input * 1000000UL, BIT(data->energy_units));
182 mutex_unlock(&data->lock);
185 static int amd_energy_read(struct device *dev,
186 enum hwmon_sensor_types type,
187 u32 attr, int channel, long *val)
189 struct amd_energy_data *data = dev_get_drvdata(dev);
192 if (channel >= data->nr_cpus) {
193 cpu = cpumask_first_and(cpu_online_mask,
195 (channel - data->nr_cpus));
196 amd_add_delta(data, channel, cpu, val, false);
199 if (!cpu_online(cpu))
202 amd_add_delta(data, channel, cpu, val, true);
208 static umode_t amd_energy_is_visible(const void *_data,
209 enum hwmon_sensor_types type,
210 u32 attr, int channel)
215 static int energy_accumulator(void *p)
217 struct amd_energy_data *data = (struct amd_energy_data *)p;
219 while (!kthread_should_stop()) {
221 * Ignoring the conditions such as
222 * cpu being offline or rdmsr failure
224 read_accumulate(data);
226 set_current_state(TASK_INTERRUPTIBLE);
227 if (kthread_should_stop())
231 * On a 240W system, with default resolution the
232 * Socket Energy status register may wrap around in
233 * 2^32*15.3 e-6/240 = 273.8041 secs (~4.5 mins)
235 * let us accumulate for every 100secs
237 schedule_timeout(msecs_to_jiffies(100000));
242 static const struct hwmon_ops amd_energy_ops = {
243 .is_visible = amd_energy_is_visible,
244 .read = amd_energy_read,
245 .read_string = amd_energy_read_labels,
248 static int amd_create_sensor(struct device *dev,
249 struct amd_energy_data *data,
252 struct hwmon_channel_info *info = &data->energy_info;
253 struct sensor_accumulator *accums;
254 int i, num_siblings, cpus, sockets;
257 /* Identify the number of siblings per core */
258 num_siblings = ((cpuid_ebx(0x8000001e) >> 8) & 0xff) + 1;
260 sockets = num_possible_nodes();
263 * Energy counter register is accessed at core level.
264 * Hence, filterout the siblings.
266 cpus = num_present_cpus() / num_siblings;
268 s_config = devm_kcalloc(dev, cpus + sockets,
269 sizeof(u32), GFP_KERNEL);
273 accums = devm_kcalloc(dev, cpus + sockets,
274 sizeof(struct sensor_accumulator),
280 info->config = s_config;
282 data->nr_cpus = cpus;
283 data->nr_socks = sockets;
284 data->accums = accums;
286 for (i = 0; i < cpus + sockets; i++) {
287 s_config[i] = config;
289 scnprintf(accums[i].label, 10,
292 scnprintf(accums[i].label, 10,
293 "Esocket%u", (i - cpus));
299 static int amd_energy_probe(struct platform_device *pdev)
301 struct device *hwmon_dev;
302 struct amd_energy_data *data;
303 struct device *dev = &pdev->dev;
305 data = devm_kzalloc(dev,
306 sizeof(struct amd_energy_data), GFP_KERNEL);
310 data->chip.ops = &amd_energy_ops;
311 data->chip.info = data->info;
313 dev_set_drvdata(dev, data);
314 /* Populate per-core energy reporting */
315 data->info[0] = &data->energy_info;
316 amd_create_sensor(dev, data, hwmon_energy,
317 HWMON_E_INPUT | HWMON_E_LABEL);
319 mutex_init(&data->lock);
320 get_energy_units(data);
322 hwmon_dev = devm_hwmon_device_register_with_info(dev, DRVNAME,
326 if (IS_ERR(hwmon_dev))
327 return PTR_ERR(hwmon_dev);
329 data->wrap_accumulate = kthread_run(energy_accumulator, data,
330 "%s", dev_name(hwmon_dev));
331 if (IS_ERR(data->wrap_accumulate))
332 return PTR_ERR(data->wrap_accumulate);
334 return PTR_ERR_OR_ZERO(data->wrap_accumulate);
337 static int amd_energy_remove(struct platform_device *pdev)
339 struct amd_energy_data *data = dev_get_drvdata(&pdev->dev);
341 if (data && data->wrap_accumulate)
342 kthread_stop(data->wrap_accumulate);
347 static const struct platform_device_id amd_energy_ids[] = {
348 { .name = DRVNAME, },
351 MODULE_DEVICE_TABLE(platform, amd_energy_ids);
353 static struct platform_driver amd_energy_driver = {
354 .probe = amd_energy_probe,
355 .remove = amd_energy_remove,
356 .id_table = amd_energy_ids,
362 static struct platform_device *amd_energy_platdev;
364 static const struct x86_cpu_id cpu_ids[] __initconst = {
365 X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x17, 0x31, NULL),
368 MODULE_DEVICE_TABLE(x86cpu, cpu_ids);
370 static int __init amd_energy_init(void)
374 if (!x86_match_cpu(cpu_ids))
377 ret = platform_driver_register(&amd_energy_driver);
381 amd_energy_platdev = platform_device_alloc(DRVNAME, 0);
382 if (!amd_energy_platdev) {
383 platform_driver_unregister(&amd_energy_driver);
387 ret = platform_device_add(amd_energy_platdev);
389 platform_device_put(amd_energy_platdev);
390 platform_driver_unregister(&amd_energy_driver);
397 static void __exit amd_energy_exit(void)
399 platform_device_unregister(amd_energy_platdev);
400 platform_driver_unregister(&amd_energy_driver);
403 module_init(amd_energy_init);
404 module_exit(amd_energy_exit);
406 MODULE_DESCRIPTION("Driver for AMD Energy reporting from RAPL MSR via HWMON interface");
407 MODULE_AUTHOR("Naveen Krishna Chatradhi <nchatrad@amd.com>");
408 MODULE_LICENSE("GPL");